Home » Java » Java 8 List<V> into Map<K, V>

Java 8 List<V> into Map<K, V>

Posted by: admin November 2, 2017 Leave a comment

Questions:

I want to translate a List of objects into a Map using Java 8’s streams and lambdas.

This is how I would write it in Java 7 and below.

private Map<String, Choice> nameMap(List<Choice> choices) {
        final Map<String, Choice> hashMap = new HashMap<>();
        for (final Choice choice : choices) {
            hashMap.put(choice.getName(), choice);
        }
        return hashMap;
}

I can accomplish this easily using Java 8 and Guava but I would like to know how to do this without Guava.

In Guava:

private Map<String, Choice> nameMap(List<Choice> choices) {
    return Maps.uniqueIndex(choices, new Function<Choice, String>() {

        @Override
        public String apply(final Choice input) {
            return input.getName();
        }
    });
}

And Guava with Java 8 lambdas.

private Map<String, Choice> nameMap(List<Choice> choices) {
    return Maps.uniqueIndex(choices, c -> c.getName());
}
Answers:

Based on Collectors documentation it’s as simple as:

Map<String, Choice> result =
    choices.stream().collect(Collectors.toMap(Choice::getName,
                                              Function.identity()));

Questions:
Answers:

If your key is NOT guaranteed to be unique for all elements in the list, you should convert it to a Map<String, List<Choice>> instead of a Map<String, Choice>

Map<String, List<Choice>> result =
 choices.stream().collect(Collectors.groupingBy(Choice::getName));

Questions:
Answers:

Use getName() as the key and choice itself as the value of the map:

Map<String, Choice> result =
    choices.stream().collect(Collectors.toMap(Choice::getName, c -> c));

Questions:
Answers:

Here’s another one in case you don’t want to use Collectors.toMap()

Map<String, Choice> result =
   choices.stream().collect(HashMap<String, Choice>::new, 
                           (m, c) -> m.put(c.getName(), c),
                           (m, u) -> {});

Questions:
Answers:

If you don’t mind to use a 3rd party lib (Vavr(formerly known as Javaslang)) you could use powerful new immutable collections:

// import javaslang.collection.*;
Map<String, Choice> map = list.toMap(choice -> Tuple.of(choice.getName(), choice));

There are also many methods to convert Java collections forth and back.

Please read more about the new collections here.

Disclaimer: I’m the creator of Vavr.

Questions:
Answers:

I was trying to do this and found that, using the answers above, when using Functions.identity() for the key to the Map, then I had issues with using a local method like this::localMethodName to actually work because of typing issues.

Functions.identity() actually does something to the typing in this case so the method would only work by returning Object and accepting a param of Object

To solve this, I ended up ditching Functions.identity() and using s->s instead.

So my code, in my case to list all directories inside a directory, and for each one use the name of the directory as the key to the map and then call a method with the directory name and return a collection of items, looks like:

Map<String, Collection<ItemType>> items = Arrays.stream(itemFilesDir.listFiles(File::isDirectory))
.map(File::getName)
.collect(Collectors.toMap(s->s, this::retrieveBrandItems));

Questions:
Answers:

If you don’t mind using 3rd party libraries, AOL’s cyclops-react lib (disclosure I am a contributor) has extensions for all JDK Collection types, including List and Map.

ListX<Choices> choices;
Map<String, Choice> map = choices.toMap(c-> c.getName(),c->c);

Questions:
Answers:

One more option in simple way

Map<String,Choice> map = new HashMap<>();
choices.forEach(e->map.put(e.getName(),e));

Questions:
Answers:
Map<String, Set<String>> collect = Arrays.asList(Locale.getAvailableLocales()).stream().collect(Collectors
                .toMap(l -> l.getDisplayCountry(), l -> Collections.singleton(l.getDisplayLanguage())));

Questions:
Answers:

I use this syntax

Map<Integer, List<Choice>> choiceMap = 
choices.stream().collect(Collectors.groupingBy(choice -> choice.getName()));

Questions:
Answers:

Here is solution by StreamEx

StreamEx.of(choices).toMap(Choice::getName, c -> c);

Questions:
Answers:

You can create a Stream of the indices using an IntStream and then convert them to a Map :

Map<Integer,Item> map = 
IntStream.range(0,items.size())
         .boxed()
         .collect(Collectors.toMap (i -> i, i -> items.get(i)));

Questions:
Answers:

For example, if you want convert object fields to map:

Example object:

class Item{
        private String code;
        private String name;

        public Item(String code, String name) {
            this.code = code;
            this.name = name;
        }

        //getters and setters
    }

And operation convert List To Map:

List<Item> list = new ArrayList<>();
list.add(new Item("code1", "name1"));
list.add(new Item("code2", "name2"));

Map<String,String> map = list.stream()
     .collect(Collectors.toMap(Item::getCode(), Item::getName()));

Questions:
Answers:
Map<String,Choice> map=list.stream().collect(Collectors.toMap(Choice::getName, s->s));

Even serves this purpose for me,

Map<String,Choice> map=  list1.stream().collect(()-> new HashMap<String,Choice>(), 
            (r,s) -> r.put(s.getString(),s),(r,s) -> r.putAll(s));