Collecting to a ConcurrentMap – Streams
By Stephen Trude / August 23, 2021 / No Comments / Certifications of Oracle, Collecting to an Array, Multilevel Partitioning
Collecting to a ConcurrentMap
If the collector returned by the Collectors.toMap() method is used in a parallel stream, the multiple partial maps created during parallel execution are merged by the collector to create the final result map. Merging maps can be expensive if keys from one map are merged into another. To address the problem, the Collectors class provides the three overloaded methods toConcurrentMap(), analogous to the three toMap() methods, that return a concurrent collector—that is, a collector that uses a single concurrent map to perform the reduction. A concurrent map is thread-safe and unordered. A concurrent map implements the java.util.concurrent.Concur-rentMap interface, which is a subinterface of java.util.Map interface (§23.7, p. 1482).
Using a concurrent map avoids merging of maps during parallel execution, as a single map is created that is used concurrently to accumulate the results from the execution of each substream. However, the concurrent map is unordered—any encounter order in the stream is ignored. Usage of the toConcurrentMap() method is illustrated by the following example of a parallel stream to create a concurrent map of CD titles released each year.
ConcurrentMap<Year, String> concMapYearToTitles = CD.cdList
.parallelStream()
.collect(Collectors.toConcurrentMap(CD::year, CD::title,
(tt, t) -> tt + “:” + t));
//{2017=Java Jam:Java Jive, 2018=Lambda Dancing:Hot Generics:Keep on Erasing}
Joining
The joining() method creates a collector for concatenating the input elements of type CharSequence to a single immutable String. However, internally it uses a mutable StringBuilder. Note that the collector returned by the joining() methods performs functional reduction, as its result is a single immutable string.
static Collector<CharSequence,?,String> joining()
static Collector<CharSequence,?,String> joining(CharSequence delimiter)
static Collector<CharSequence,?,String> joining(CharSequence delimiter,
CharSequence prefix,
CharSequence suffix)
Return a Collector that concatenates CharSequence elements into a String. The first method concatenates in encounter order. So does the second method, but this method separates the elements by the specified delimiter. The third method in addition applies the specified prefix and suffix to the result of the concatenation.
The wildcard ? is a type parameter that is used internally by the collector.
The methods preserve the encounter order, if the stream has one.
Among the classes that implement the CharSequence interface are the String, StringBuffer, and StringBuilder classes.
The stream pipelines below concatenate CD titles to illustrate the three overloaded joining() methods. The CharSequence elements are Strings. The strings are concatenated in the stream encounter order, which is the positional order for lists. The zero-argument joining() method at (1) performs string concatenation of the CD titles using a StringBuilder internally, and returns the result as a string.
String concatTitles1 = CD.cdList.stream() // Stream<CD>
.map(CD::title) // Stream<String>
.collect(Collectors.joining()); // (1)
//Java JiveJava JamLambda DancingKeep on ErasingHot Generics
The single-argument joining() method at (2) concatenates the titles using the specified delimiter.
String concatTitles2 = CD.cdList.stream()
.map(CD::title)
.collect(Collectors.joining(“, “)); // (2) Delimiter
//Java Jive, Java Jam, Lambda Dancing, Keep on Erasing, Hot Generics
The three-argument joining() method at (3) concatenates the titles using the specified delimiter, prefix, and suffix.
String concatTitles3 = CD.cdList.stream()
.map(CD::title)
.collect(Collectors.joining(“, “, “[“, “]”)); // (3) Delimiter, Prefix, Suffix
//[Java Jive, Java Jam, Lambda Dancing, Keep on Erasing, Hot Generics]