Элегантно создайте карту с полями объекта как ключ / значение из потока объекта в Java 8
У меня есть следующий класс
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;}
}
Как мне создать карту возраста для хобби, как Map<Integer, Set<String>>
?
Java 8, как я приготовил это:
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())
)
);
Есть ли более идиоматический способ сделать это с Collectors.groupingBy()
возможно?
Как связанный вопрос, я считаю, что версия без потоков Java является более удобочитаемой.
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);
}
Должны ли мы идти с не потоковым кодом, если его легче читать; особенно, когда потоковая версия, как видно здесь, не имеет промежуточных потоков с преобразованиями данных, но быстро заканчивается операцией терминала?
2 ответа
Как вы сами отметили: Stream
решение может быть не таким читаемым, как ваше текущееStream
-решение. Решение вашей проблемы с groupingBy
может выглядеть не так хорошо, как вы ожидаете, так как вы хотите превратить ваш List
в Set
,
Я построил решение с groupingBy
, mapping
а также reducing
, но это решение не так легко прочитать и даже содержит ошибку. Вы можете узнать больше об этом в: Java 8 stream.collect (... groupingBy (... mapping (... сокращения))), сокращающий использование BinaryOperator. Я действительно предлагаю посмотреть ответ, который дал Холгер, так как он также содержит более простое решение с использованием пользовательских Collector
и немного Outlook для Java 9 flatMapping
что для меня приближается к вашемуStream
-решение.
Но другое решение, использующее groupingBy
Я придумал, и это на самом деле работает так:
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())));
для этого вам понадобится следующий импорт:
import java.util.AbstractMap.SimpleEntry;
import java.util.Map.Entry;
Конечно, вы можете взять также Tuple
или же Pair
или что тебе нравится больше всего.
Но опять же не лучше в любом случае.
Я бы остался с твоим нынешнимStream
-решение. Он более читабелен и делает то, что должен.
Посмотрите на подобный пример из документации Java 8 Collectors:
Map<City, Set<String>> namesByCity
= people.stream().collect(groupingBy(Person::getCity, TreeMap::new,
mapping(Person::getLastName, toSet())));
Вы можете использовать тот же подход здесь.