Collectors in Java Streams

3 minute read

Also Check : Collectors - Deep Dive

Collectors (from java.util.stream.Collectors package) are used to perform mutable reduction operations on the elements of a stream, transforming them into different data structures or aggregating their values.

collect

  • Collect the data into a list using
    • .collect(Collectors.toList()) or just .toList()
  • Pass a key mapper function and value mapper function to create a map
    • .collect(Collectors.toMap(Function.identity(), String::length))
  • If data need to be passed to a client, it’s a good idea to use unmodifiable collection
    • .collect(Collectors.toUnmodifiableList())
  • Returns an immutable list containing only one specified object(singleton)
    • Collections.singletonList(s)
          parentDto.setStringList(Collections.singletonList(businessDto.getStringList()));
      

Grouping groupingBy

Categorize elements of a stream based on a classification function. It returns a Map where

  • the keys are the result of applying the classification function (takes a function as first parameter),
  • and the values are lists of items that match the classification. It’s another Collector that can have the values.
    • The collector can be another operation that returns a collector like filtering, mapping, filtering etc.

Single-Argument groupingBy: Uses the classifier function and defaults to collecting elements into a List.

Two-Argument groupingBy: Uses the classifier function and a specified downstream collector to determine how the grouped elements are collected.

Categorize a list of strings based on their length.

List<String> strings = Arrays.asList("apple", "banana", "cherry", "date");
// The Map will have the lengths as keys and lists of strings with those lengths as values
Map<Integer, List<String>> categorizedByLength = strings.stream()
        //.collect(Collectors.groupingBy(str -> str.length(), Collectors.toList()));//Two-Argument groupingBy: Uses the classifier function and a specified downstream collector to determine how the grouped elements are collected.
        .collect(Collectors.groupingBy(String::length));//Single-Argument groupingBy: Uses the classifier function and defaults to collecting elements into a List.

System.out.println(categorizedByLength);//{4=[date], 5=[apple], 6=[banana, cherry]}

Categorize a list of strings based on their length and count the number of strings in each category.

List<String> strings = Arrays.asList("apple", "banana", "cherry", "date");
Map<Integer, Long> countedByLength = strings.stream()
      .collect(Collectors.groupingBy(String::length, Collectors.counting()));
//    .collect(Collectors.groupingBy(String::length, 
//            Collectors.collectingAndThen(Collectors.mapping(s -> s, Collectors.toList()),
//                    list -> (long) list.size())
//    ));

Count Frequency of each integer

List<Integer> list = Arrays.asList(1,2,1,3,3,4,5,6,7,8,6,5,4,3,2,1);

//Find frequency of all the numbers
Map<Integer,Long> map = list.stream()
     //.collect(groupingBy(element -> element, counting()));// Function.identity() Equivalent to an i in a for loop
       .collect(Collectors.groupingBy(Function.identity(), counting()));//collect takes a COLLECTOR as parameter. any method that returns a collector can be used

System.out.println(map);//{1=3, 2=2, 3=3, 4=2, 5=2, 6=2, 7=1, 8=1}

Partitioning - partitioningBy

Split the elements of a stream into two groups based on a predicate.

Collectors.partitioningBy accepts a predicate and returns a map

  • with one key for true with all the values with true results and
  • another with false results.
  • The values are lists of items that match or do not match the predicate

Partition a list of integers into even and odd numbers.

List<Integer> list = Arrays.asList(1,2,1,3,3,4,5,6,7,8,6,5,4,3,2,1);
Map<Boolean, List<Integer>> collect = list.stream()
        .collect(partitioningBy(number -> number % 2 == 0));
        //.collect(partitioningBy(evenAgedEmpPredicate));//Predicate can be extracted out and can be passed as an argument

System.out.println(collect);//{false=[1, 1, 3, 3, 5, 7, 5, 3, 1], true=[2, 4, 6, 8, 6, 4, 2]}

Filtering

Collectors.filtering() takes a predicate as a first argument and another Collector as second argument.

List<Integer> list = List.of(1,2,1,3,3,4,5,6,7,8,6,5,4,3,2,1);
List<Integer> evenNumberList = list.stream()
      .collect(Collectors.filtering(number -> number % 2 == 0, toList()));

System.out.println(evenNumberList);//[2, 4, 6, 8, 6, 4, 2]

Mapping

Collectors.mapping takes a function (as first parameter) based on which the transformation happens and a Collector as second parameter

With the mapping function, use Stream.of() or collections.stream()

List<Integer> list = List.of(1,2,1,3,3,4,5,6,7,8,6,5,4,3,2,1);

List<Integer> doubleNumberList = list.stream()
        .distinct()//Finds unique elements 
        .collect(Collectors.mapping(number -> number * 2 , Collectors.toList()));
System.out.println(doubleNumberList);//[2, 4, 6, 8, 10, 12, 14, 16]

Flat Mapping

Collectors.flatMapping takes a Stream as first input and takes Collector as second parameter.

The method signatures of map from Stream and flatMap of Collectors looks like below

 <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)
 <R> Stream<R> map    (Function<? super T, ? extends R> mapper)
  • flatMapping applies the map first and then does the flattening.
List<String> list = List.of("one", "two wings", "three tyres", "four turbo combustion engine");
//Fnd a list of each word separated without space
List<String> collect = list.stream()
        .collect(flatMapping(str -> Stream.of(str.split(" ")), toList()));

System.out.println(collect);//[one, two, wings, three, tyres, four, turbo, combustion, engine]

counting

Counts the number of elements in a stream.

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

List<String> strings = Arrays.asList("apple", "banana", "cherry");
long count = strings.stream()
    .collect(Collectors.counting());

mapping

Applies a function to each element of a stream and collects the results using another collector.

List<String> strings = Arrays.asList("apple", "banana", "cherry", "date");
Map<Integer, List<String>> uppercaseByLength = strings.stream()
    .collect(Collectors.groupingBy(String::length, Collectors.mapping(String::toUpperCase, Collectors.toList())));

joining

Concatenates the elements of a stream into a single String, with an optional delimiter, prefix, and suffix.

List<String> strings = Arrays.asList("apple", "banana", "cherry");
String joined = strings.stream()
    .collect(Collectors.joining(", "));

Tags:

Categories:

Updated: