it-swarm.com.ru

Используя потоки, как я могу отобразить значения в HashMap?

Учитывая Map<String, Person>, где Person имеет метод String getName() (etc), как я могу превратить Map<String, Person> в Map<String, String>, где String получается из вызова Person::getName()?

Pre-Java 8 я бы использовал

Map<String, String> byNameMap = new HashMap<>();

for (Map.Entry<String, Person> person : people.entrySet()) {
    byNameMap.put(person.getKey(), person.getValue().getName());
}

но я бы хотел сделать это с помощью потоков и лямбд.

Я не вижу, как это сделать в функциональном стиле: Map/HashMap не реализует Stream .

people.entrySet() возвращает Set<Entry<String, Person>>, который я могу передавать по потоку, но как я могу добавить новый Entry<String, String> на карту назначения?

20
David Kerr

С Java 8 вы можете сделать:

Map<String, String> byNameMap = new HashMap<>();
people.forEach((k, v) -> byNameMap.put(k, v.getName());

Хотя было бы лучше использовать Guava Maps.transformValues , который оборачивает оригинальное Map и выполняет преобразование, когда вы делаете get, то есть вы оплачиваете стоимость преобразования только тогда, когда вы фактически используете это значение.

Использование Guava будет выглядеть так:

Map<String, String> byNameMap = Maps.transformValues(people, Person::getName);

EDIT:

После комментария @ Eelco (и для полноты) преобразование в карту лучше выполнить с помощью Collectors.toMap следующим образом:

Map<String, String> byNameMap = people.entrySet()
  .stream()
  .collect(Collectors.toMap(Map.Entry::getKey, (entry) -> entry.getValue().getName());
25
Nick Holt

Одним из способов является использование toMap сборщика:

import static Java.util.stream.Collectors.toMap;

Map<String, String> byNameMap = people.entrySet().stream()
                                     .collect(toMap(Entry::getKey, 
                                                    e -> e.getValue().getName()));
19
assylias

Используя немного общего кода, который я, к сожалению, не нашел в библиотеках, которые у меня были под рукой

public static <K, V1, V2> Map<K, V2> remap(Map<K, V1> map,
        Function<? super V1, ? extends V2> function) {

    return map.entrySet()
            .stream() // or parallel
            .collect(Collectors.toMap(
                    Map.Entry::getKey, 
                    e -> function.apply(e.getValue())
                ));
}

По сути, это то же самое, что и у Guavas Maps.transformValues за минусом минусов, упомянутых другими.

Map<String, Person> persons = ...;
Map<String, String> byNameMap = remap(persons, Person::getName);

И в случае, если вам нужен ключ, а также значение в вашей функции переназначения, эта вторая версия делает это возможным

public static <K, V1, V2> Map<K, V2> remap(Map<K, V1> map,
        BiFunction<? super K, ? super V1, ? extends V2> function) {

    return map.entrySet()
            .stream() // or parallel
            .collect(Collectors.toMap(
                    Map.Entry::getKey,
                    e -> function.apply(e.getKey(), e.getValue())
                ));
}

Это может быть использовано, например, как

Map<String, String> byNameMap = remap(persons, (key, val) -> key + ":" + val.getName());
0
zapl