186. Collecting the result of a stream

Let's assume that we have the following Melon class:

public class Melon {

private String type;
private int weight;

// constructors, getters, setters, equals(),
// hashCode(), toString() omitted for brevity
}

Let's also assume that we have the List of Melon:

List<Melon> melons = Arrays.asList(new Melon("Crenshaw", 2000),
new Melon("Hemi", 1600), new Melon("Gac", 3000),
new Melon("Apollo", 2000), new Melon("Horned", 1700),
new Melon("Gac", 3000), new Melon("Cantaloupe", 2600));

Typically, a stream pipeline ends up with a summary of the elements in the stream. In other words, we need to collect the results in a data structure such as List, Set, or Map (and their companions).

For accomplishing this task, we can rely on the Stream.collect​(Collector<? super T,​A,​R> collector) method. This method gets a single argument representing a java.util.stream.Collector or a user-defined Collector.

The most famous collectors include the following:

  • toList()
  • toSet()
  • toMap()
  • toCollection()

Their names speak for themselves. Let's take a look at several examples:

  • Filter melons that are heavier than 1,000 g and collect the result in a List via toList() and toCollection():
List<Integer> resultToList = melons.stream()
.map(Melon::getWeight)
.filter(x -> x >= 1000)
.collect(Collectors.toList());

List<Integer> resultToList = melons.stream()
.map(Melon::getWeight)
.filter(x -> x >= 1000)
.collect(Collectors.toCollection(ArrayList::new));

The argument of the toCollection() method is a Supplier that provides a new empty Collection into which the results will be inserted.

  • Filter melons that are heavier than 1,000 g and collect the result without duplicates in a Set via toSet() and toCollection():
Set<Integer> resultToSet = melons.stream()
.map(Melon::getWeight)
.filter(x -> x >= 1000)
.collect(Collectors.toSet());

Set<Integer> resultToSet = melons.stream()
.map(Melon::getWeight)
.filter(x -> x >= 1000)
.collect(Collectors.toCollection(HashSet::new));
  • Filter melons that are heavier than 1,000 grams, collect the result without duplicates, and sort into ascending order in a Set via toCollection():
Set<Integer> resultToSet = melons.stream()
.map(Melon::getWeight)
.filter(x -> x >= 1000)
.collect(Collectors.toCollection(TreeSet::new));
  • Filter a distinct Melon and collect the result in a Map<String, Integer> via toMap():
Map<String, Integer> resultToMap = melons.stream()
.distinct()
.collect(Collectors.toMap(Melon::getType,
Melon::getWeight));

The two arguments of the toMap() method represent a mapping function that's used to produce keys and their respective values (this is prone to the java.lang.IllegalStateException duplicate key exception if two Melon have the same key).

  • Filter a distinct Melon and collect the result in a Map<Integer, Integer> via toMap() using random keys (prone to the java.lang.IllegalStateException duplicate key if two identical keys are generated):
Map<Integer, Integer> resultToMap = melons.stream()
.distinct()
.map(x -> Map.entry(
new Random().nextInt(Integer.MAX_VALUE), x.getWeight()))
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
  • Collect a Melon in a map via toMap() and avoid the potential java.lang.IllegalStateException duplicate key by choosing the existing (old) value in case of a key collision:
Map<String, Integer> resultToMap = melons.stream()
.collect(Collectors.toMap(Melon::getType, Melon::getWeight,
(oldValue, newValue) -> oldValue));

The last argument of the toMap() method is a merge function and is used to resolve collisions between values associated with the same key, as supplied to Map.merge(Object, Object, BiFunction).

Obviously, choosing the new value can be done with (oldValue, newValue) -> newValue:

  • Put the preceding example into a sorted Map (for example, by weight):
Map<String, Integer> resultToMap = melons.stream()
.sorted(Comparator.comparingInt(Melon::getWeight))
.collect(Collectors.toMap(Melon::getType, Melon::getWeight,
(oldValue, newValue) -> oldValue,
LinkedHashMap::new));

The last argument of this toMap() flavor represents a Supplier that provides a new empty Map into which the results will be inserted. In this example, this Supplier is needed to preserve the order after sorting. Since HashMap doesn't guarantee the order of insertion, we need to rely on LinkedHashMap.

  • Collect the word frequency count via toMap():
String str = "Lorem Ipsum is simply 
Ipsum Lorem not simply Ipsum";

Map<String, Integer> mapOfWords = Stream.of(str)
.map(w -> w.split("\s+"))
.flatMap(Arrays::stream)
.collect(Collectors.toMap(
w -> w.toLowerCase(), w -> 1, Integer::sum));
Beside toList(), toMap(), and toSet(), the Collectors class also exposes collectors to unmodifiable and concurrent collections such as toUnmodifiableList(), toConcurrentMap(), and so on.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset