Элегантно создайте карту с полями объекта как ключ / значение из потока объекта в 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())));

Вы можете использовать тот же подход здесь.

Другие вопросы по тегам