Skip to content
The Code Storm
All articles
Stream Storm2 min read

Java Streams: distinct, limit, reduce and groupingBy

Remove repeats, stop early, fold a list into one value, and sort items into groups, with the output of every example.

Part 1 covered the steps you use to reshape a list. This part adds four more tools: one that removes repeats, one that stops early, one that folds many items into one, and one that sorts items into groups.

distinct: remove repeats

distinct() keeps the first copy of each item and drops the later ones:

java
List<Integer> nums = List.of(3, 1, 3, 2, 1);
 
nums.stream().distinct().toList();
// [3, 1, 2]

Three is new, so it stays. One is new. The second three and the second one are already seen, so they go. The order of first appearance is kept.

limit: stop after a few items

limit(n) keeps the first n items and then stops asking for more. That makes it safe to use on a stream that never ends:

java
Stream.iterate(1, n -> n + 1).limit(3).toList();
// [1, 2, 3]

Without limit, that stream would count up forever.

reduce: many items in, one value out

reduce takes a starting value and a rule for combining two items. It applies the rule one item at a time:

java
Stream.of(3, 5, 2, 4).reduce(0, Integer::sum);
// 14

Start at 0, add 3 to get 3, add 5 to get 8, add 2 to get 10, add 4 to get 14.

Leave out the starting value and the result comes back in an Optional, because an empty stream has nothing to combine:

java
Stream.of(3, 5, 2, 4).reduce(Integer::sum);    // Optional[14]
Stream.<Integer>empty().reduce(Integer::sum);  // Optional.empty

groupingBy: sort items into buckets

groupingBy takes a function that picks a key for each item, and gives you a map from each key to the items that share it. Here the key is the length of the word:

java
List<String> words = List.of("kiwi", "fig", "apple", "pear", "mango");
 
Map<Integer, List<String>> byLength = words.stream()
    .collect(Collectors.groupingBy(String::length));
// {3=[fig], 4=[kiwi, pear], 5=[apple, mango]}

Want only the size of each group?

Pass a second collector. counting() turns each group into a number:

java
words.stream().collect(Collectors.groupingBy(String::length, Collectors.counting()));
// {3=1, 4=2, 5=2}

mapping changes the items inside each group before they are collected:

java
words.stream().collect(Collectors.groupingBy(
    String::length,
    Collectors.mapping(String::toUpperCase, Collectors.toList())));
// {3=[FIG], 4=[KIWI, PEAR], 5=[APPLE, MANGO]}

All output above was produced by running the code on Java 21.

Get the next one in your inbox

New posts, videos and free PDFs. One email a week at most.