Collectors Utility Class

About

In Java, the Collectors class is a part of the java.util.stream package and provides a variety of utility methods to perform reduction operations on streams. These methods are used to accumulate elements from a stream into a collection or to summarize elements in various ways.

Basic Syntax

stream.collect(Collectors.someCollectorMethod());
  • stream → The Stream we want to collect data from.

  • collect(Collectors.someMethod()) → Applies a reduction operation.

Collecting into Lists, Sets, and Maps

Method
Description

toList()

Collects elements into an unmodifiable List

toUnmodifiableList()

Returns an immutable List

toSet()

Collects elements into an unmodifiable Set

toUnmodifiableSet()

Returns an immutable Set

toMap(keyMapper, valueMapper)

Collects elements into a Map (throws error for duplicate keys)

toMap(keyMapper, valueMapper, mergeFunction)

Collects into a Map, merging duplicates

toMap(keyMapper, valueMapper, mergeFunction, mapSupplier)

Collects into a custom Map implementation

What is Function.identity()?

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

public class CollectorsExample {
    public static void main(String[] args) {
        List<String> names = List.of("Alice", "Bob", "Charlie", "Alice", "David");

        // 1. Collecting into an unmodifiable List
        List<String> list1 = names.stream().collect(Collectors.toList());

        // 2. Collecting into an immutable List
        List<String> list2 = names.stream().collect(Collectors.toUnmodifiableList());

        // 3. Collecting into an unmodifiable Set
        Set<String> set1 = names.stream().collect(Collectors.toSet());

        // 4. Collecting into an immutable Set
        Set<String> set2 = names.stream().collect(Collectors.toUnmodifiableSet());

        // 5. Collecting into a Map (throws error for duplicate keys)
        try {
            Map<String, Integer> map1 = names.stream()
                    .collect(Collectors.toMap(Function.identity(), String::length));
        } catch (IllegalStateException e) {
            System.out.println("toMap(keyMapper, valueMapper) failed due to duplicate keys.");
        }

        // 6. Collecting into a Map with merging function (in case of duplicate keys, keep the longest name)
        Map<String, Integer> map2 = names.stream()
                .collect(Collectors.toMap(Function.identity(), String::length, Integer::max));

        // 7. Collecting into a custom Map (LinkedHashMap to maintain insertion order)
        Map<String, Integer> map3 = names.stream()
                .collect(Collectors.toMap(Function.identity(), String::length, Integer::max, LinkedHashMap::new));
    }
}

Aggregation and Summarization

Method
Description

counting()

Counts the number of elements

summarizingInt(ToIntFunction<T>)

Returns IntSummaryStatistics (count, min, max, sum, avg)

summarizingDouble(ToDoubleFunction<T>)

Returns DoubleSummaryStatistics

summarizingLong(ToLongFunction<T>)

Returns LongSummaryStatistics

maxBy(Comparator<T>)

Returns the maximum element

minBy(Comparator<T>)

Returns the minimum element

reducing(BinaryOperator<T>)

Performs a general reduction

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class CollectorsExample {
    public static void main(String[] args) {
        List<Integer> numbers = List.of(10, 20, 30, 40, 50, 10, 20);

        // 1. Counting the number of elements
        long count = numbers.stream().collect(Collectors.counting());

        // 2. Summarizing Int (count, min, max, sum, avg)
        IntSummaryStatistics intStats = numbers.stream().collect(Collectors.summarizingInt(Integer::intValue));

        // 3. Summarizing Double
        DoubleSummaryStatistics doubleStats = numbers.stream().collect(Collectors.summarizingDouble(Double::valueOf));

        // 4. Summarizing Long
        LongSummaryStatistics longStats = numbers.stream().collect(Collectors.summarizingLong(Long::valueOf));

        // 5. Finding maximum element
        numbers.stream().collect(Collectors.maxBy(Integer::compareTo))
                .ifPresent(max -> System.out.println("maxBy(): " + max));

        // 6. Finding minimum element
        numbers.stream().collect(Collectors.minBy(Integer::compareTo))
                .ifPresent(min -> System.out.println("minBy(): " + min));

        // 7. Reducing (sum of elements)
        int sum = numbers.stream().collect(Collectors.reducing(0, Integer::intValue, Integer::sum));
    }
}

Joining Strings

Method
Description

joining()

Concatenates elements into a single string

joining(delimiter)

Joins elements with a delimiter

joining(delimiter, prefix, suffix)

Joins with a delimiter, prefix, and suffix

import java.util.List;
import java.util.stream.Collectors;

public class CollectorsJoiningExample {
    public static void main(String[] args) {
        List<String> words = List.of("Apple", "Banana", "Cherry");

        // 1. Joining elements into a single string
        String joined1 = words.stream().collect(Collectors.joining());
        System.out.println("joining(): " + joined1);  
        // Output: joining(): AppleBananaCherry

        // 2. Joining elements with a delimiter
        String joined2 = words.stream().collect(Collectors.joining(", "));
        System.out.println("joining(delimiter): " + joined2);  
        // Output: joining(delimiter): Apple, Banana, Cherry

        // 3. Joining with a delimiter, prefix, and suffix
        String joined3 = words.stream().collect(Collectors.joining(", ", "[", "]"));
        System.out.println("joining(delimiter, prefix, suffix): " + joined3);  
        // Output: joining(delimiter, prefix, suffix): [Apple, Banana, Cherry]
    }
}

Grouping & Partitioning

Method
Description

groupingBy(classifier)

Groups elements into a Map<K, List<T>>

groupingBy(classifier, collector)

Groups with a custom downstream collector

groupingBy(classifier, supplier, collector)

Groups elements into a custom Map implementation

partitioningBy(predicate)

Splits elements into two groups (true/false)

partitioningBy(predicate, downstream)

Splits elements into two groups (true/false) with a downstream collector

import java.util.*;
import java.util.stream.Collectors;

public class CollectorsGroupingPartitioningExample {
    public static void main(String[] args) {
        List<String> words = List.of("apple", "banana", "cherry", "avocado", "blueberry", "apricot");

        // 1. groupingBy(classifier) - Groups words by their first letter
        Map<Character, List<String>> groupedByFirstLetter = words.stream()
                .collect(Collectors.groupingBy(word -> word.charAt(0)));
        System.out.println("groupingBy(classifier): " + groupedByFirstLetter);
        // Output: {a=[apple, avocado, apricot], b=[banana, blueberry], c=[cherry]}

        // 2. groupingBy(classifier, collector) - Groups words by length and collects into a Set
        Map<Integer, Set<String>> groupedByLength = words.stream()
                .collect(Collectors.groupingBy(String::length, Collectors.toSet()));
        System.out.println("groupingBy(classifier, collector): " + groupedByLength);
        // Output: {6=[banana, cherry], 5=[apple], 7=[avocado], 9=[blueberry, apricot]}

        // 3. groupingBy(classifier, supplier, collector) - Uses LinkedHashMap to maintain order
        Map<Character, List<String>> linkedGroupedByFirstLetter = words.stream()
                .collect(Collectors.groupingBy(word -> word.charAt(0), LinkedHashMap::new, Collectors.toList()));
        System.out.println("groupingBy(classifier, supplier, collector): " + linkedGroupedByFirstLetter);
        // Output: {a=[apple, avocado, apricot], b=[banana, blueberry], c=[cherry]}

        // 4. partitioningBy(predicate) - Splits words into two groups: length > 6 (true) and <= 6 (false)
        Map<Boolean, List<String>> partitionedByLength = words.stream()
                .collect(Collectors.partitioningBy(word -> word.length() > 6));
        System.out.println("partitioningBy(predicate): " + partitionedByLength);
        // Output: {false=[apple, banana, cherry], true=[avocado, blueberry, apricot]}

        // 5. partitioningBy(predicate, downstream) - Partitions words by length and collects into Sets
        Map<Boolean, Set<String>> partitionedByLengthSet = words.stream()
                .collect(Collectors.partitioningBy(word -> word.length() > 6, Collectors.toSet()));
        System.out.println("partitioningBy(predicate, downstream): " + partitionedByLengthSet);
        // Output: {false=[apple, banana, cherry], true=[avocado, blueberry, apricot]}
    }
}

Custom Reduction (reducing)

Method
Description

reducing(BinaryOperator<T>)

Reduces elements using custom logic

reducing(identity, BinaryOperator<T>)

Reduces elements with a default value

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

public class CollectorsReducingExample {
    public static void main(String[] args) {
        List<Integer> numbers = List.of(10, 20, 30, 40, 50);

        // 1. reducing(BinaryOperator<T>) - Reduces elements using a custom logic (sum in this case)
        Optional<Integer> sumOptional = numbers.stream()
                .collect(Collectors.reducing((a, b) -> a + b));
        System.out.println("reducing(BinaryOperator<T>): " + sumOptional.orElse(0));
        // Output: reducing(BinaryOperator<T>): 150

        // 2. reducing(identity, BinaryOperator<T>) - Reduces with a default value (identity = 0)
        int sumWithIdentity = numbers.stream()
                .collect(Collectors.reducing(0, (a, b) -> a + b));
        System.out.println("reducing(identity, BinaryOperator<T>): " + sumWithIdentity);
        // Output: reducing(identity, BinaryOperator<T>): 150

        // Example of reducing to find max value
        Optional<Integer> maxOptional = numbers.stream()
                .collect(Collectors.reducing(Integer::max));
        System.out.println("reducing(BinaryOperator<T>) to find max: " + maxOptional.orElse(0));
        // Output: reducing(BinaryOperator<T>) to find max: 50

        // Example of reducing to find min value with identity
        int minWithIdentity = numbers.stream()
                .collect(Collectors.reducing(Integer.MAX_VALUE, Integer::min));
        System.out.println("reducing(identity, BinaryOperator<T>) to find min: " + minWithIdentity);
        // Output: reducing(identity, BinaryOperator<T>) to find min: 10
    }
}

Last updated

Was this helpful?