David Kerr David Kerr - 4 months ago 21
Java Question

Using streams, how can I map the values in a HashMap?

Given a

Map<String, Person>
where Person has a
String getName()
(etc) method on it, how can I turn the
Map<String, Person>
into a
Map<String, String>
where the
String
is obtained from calling
Person::getName()
?

Pre-Java 8 I'd use

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

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


but I'd like to do it using streams and lambdas.

I can't see how to do this in a functional style: Map/HashMap don't implement
Stream
.

people.entrySet()
returns a
Set<Entry<String, Person>>
which I can stream over, but how can I add a new
Entry<String, String>
to the destination map?

Answer

With Java 8 you can do:

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

Though you'd be better off using Guava's Maps.transformValues, which wraps the original Map and does the conversion when you do the get, meaning you only pay the conversion cost when you actually consume the value.

Using Guava would look like this:

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

EDIT:

Following @Eelco's comment (and for completeness), the conversion to a map is better down with Collectors.toMap like this:

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