Elegantly create a map with object fields as a key / value from an object stream in Java 8

I have the following class

class Person { public String name; public int age; public List<String> hobbies; Person(String name, int age, List<String> hobbies) {this.name = name; this.age = age; this.hobbies = hobbies;} } 

How to create an age map for a hobby, for example Map<Integer, Set<String>> ?

I developed a Java 8 method:

 Map<Integer, Set<String>> collect8 = persons.stream() .collect( toMap( p -> p.age, p -> p.hobbies.stream().collect(toSet()), (hobbies1, hobbies2) -> Stream.concat(hobbies1.stream(), hobbies2.stream()).collect(toSet()) ) ); 

Is there a more idiomatic way to do this with Collectors.groupingBy() , perhaps?

As a related question, I believe that the version without Java threads is becoming more readable.

 Map<Integer, Set<String>> collect7 = new HashMap<>(); for(Person p: persons) { Set<String> hobbies = collect7.getOrDefault(p.age, new HashSet<>()); hobbies.addAll(p.hobbies); collect7.put(p.age, hobbies); } 

Should we go with non streams code if it is easier to read; especially when the streaming version, as can be seen here, does not have intermediate streams with data transformations, but quickly ends in a terminal operation?

+2
source share
2 answers

As you yourself noted: Stream solution may not be as readable as your current version of Stream . Solving your groupingBy problem may not look as good as you might expect, since you want to convert your List to Set .

I built a solution with groupingBy , mapping and reducing , but this solution is not so easy to read and even contained an error. You can learn more about this in: Java 8 stream.collect (... groupingBy (... mapping (... reduction))) reduction in the use of BinaryOperator I really suggest finding the answer that Holger gave as it also contains a simpler solution using custom Collector and a bit of Outlook for Java 9 flatMapping , which for me comes close to your Stream solution.

But I came up with another solution using groupingBy , and actually it works:

 Map<Integer, Set<String>> yourmap; yourmap = personList.stream() .flatMap(p -> p.hobbies.stream() .flatMap(hobby -> Stream.of(new SimpleEntry<>(p.age, hobby))) ) .collect(Collectors.groupingBy(Entry::getKey, Collectors.mapping(Entry::getValue, Collectors.toSet()))); 

for this you need the following import data:

 import java.util.AbstractMap.SimpleEntry; import java.util.Map.Entry; 

Of course, you can also take Tuple or Pair or whatever you like best.

But again, no better anyway.

I would stay with your current Stream solution. It is more readable and does what it should do.

+2
source

Look at a similar example from the Java 8 Collectors documentation:

 Map<City, Set<String>> namesByCity = people.stream().collect(groupingBy(Person::getCity, TreeMap::new, mapping(Person::getLastName, toSet()))); 

You can use the same approach here.

+2
source

Source: https://habr.com/ru/post/1265836/


All Articles