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

Учитывая Map<String, Person>, где Person имеет метод String getName() (и т.д.), как я могу превратить 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> к карте назначения?

Ответ 1

С помощью 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());

Ответ 2

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

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

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