Streams: Collecting into Maps in Java

Suppose you have a Stream<Person> and want to collect the elements into a map so that later you can look up people by their ID. The Collectors.toMap method has two function arguments that produce the map’s keys and values. For example,

Map<Integer, String> idToName = people.collect(

Collectors.toMap(Person::getId, Person::getName));

In the common case when the values should be the actual elements, use Function.identity() for the second function.

Map<Integer, Person> idToPerson = people.coUect(

Collectors.toMap(Person::getId, Function.identity()));

If there is more than one element with the same key, there is a conflict, and the collector will throw an IllegalStateException. You can override that behavior by supplying a third function argument that resolves the conflict and deter­mines the value for the key, given the existing and the new value. Your function could return the existing value, the new value, or a combination of them.

Here, we construct a map that contains, for each language in the available locales, as key its name in your default locale (such as “German”), and as value its localized name (such as “Deutsch”).

Stream<Locale> locales = Stream.of(Locale.getAvailableLocales());

Map<String, String> languageNames = locales.collect(

Collectors.toMap(

Locale::getDisplayLanguage,

loc -> loc.getDisplayLanguage(loc),

(existingValue, newValue) -> existingValue));

We don’t care that the same language might occur twice (for example, German in Germany and in Switzerland), so we just keep the first entry.

Now suppose we want to know all languages in a given country. Then we need a Map<String, Set<String>>. For example, the value for “Switzerland” is the set [French, German, Italian]. At first, we store a singleton set for each language. Whenever a new language is found for a given country, we form the union of the existing and the new set.

Map<String, Set<String>> countryLanguageSets = tocates.cottect(

Cottectors.toMap(

Locale::getDisplayCountry,

l-> CoUections.singteton(t.getDisptayLanguageO),

(a, b) -> { // Union of a and b

var union = new HashSet<String>(a);

union.addAU(b);

return union; }));

You will see a simpler way of obtaining this map in the next section.

If you want a TreeMap, supply the constructor as the fourth argument. You must provide a merge function. Here is one of the examples from the beginning of the section, now yielding a TreeMap:

Map<Integer, Person> idToPerson = people.coUect(

Collectors.toMap(

Person::getId,

Function.identity(),

(existingValue, newValue) -> { throw new IUegalStateExceptionO; },

TreeMap::new));

The program in Listing 1.5 gives examples of collecting stream results into maps.

Source: Horstmann Cay S. (2019), Core Java. Volume II – Advanced Features, Pearson; 11th edition.

Leave a Reply

Your email address will not be published. Required fields are marked *