🎓 A1Learner
🔍
0 of 30 lessons completed

Java Grouping and Partitioning

A downstream collector decides what happens inside each group after a stream splits. counting, mapping and partitioningBy cover the counting, adapting and splitting forms of that job.

groupingBy with counting counts each group

The classifier gives each word its length, and counting counts the words in each length group. The entries print sorted by key, so the listing has a fixed order.

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

public class Main {
  public static void main(String[] args) {
    List<String> words = List.of("cat", "dog", "elephant", "sun", "moon", "fish");
    Map<Integer, Long> counts = words.stream()
        .collect(Collectors.groupingBy(String::length, Collectors.counting()));
    counts.entrySet().stream()
        .sorted(Map.Entry.comparingByKey())
        .forEach(entry -> System.out.println(entry.getKey() + ": " + entry.getValue()));
  }
}

groupingBy with mapping collects names per letter

The classifier gives each name its starting letter. mapping passes each name through unchanged to a list collector for its group.

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

public class Main {
  public static void main(String[] args) {
    List<String> names = List.of("Ana", "Adam", "Ben", "Bella", "Cara");
    Map<Character, List<String>> byLetter = names.stream()
        .collect(Collectors.groupingBy(name -> name.charAt(0),
            Collectors.mapping(name -> name, Collectors.toList())));
    byLetter.entrySet().stream()
        .sorted(Map.Entry.comparingByKey())
        .forEach(entry -> System.out.println(entry.getKey() + ": " + entry.getValue()));
  }
}

partitioningBy splits passed from failed

partitioningBy tests each score against a pass mark of 60. Both lists print in encounter order, the order the scores arrived in.

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

public class Main {
  public static void main(String[] args) {
    List<Integer> scores = List.of(45, 82, 67, 90, 38);
    Map<Boolean, List<Integer>> parts = scores.stream()
        .collect(Collectors.partitioningBy(score -> score >= 60));
    System.out.println("Passed: " + parts.get(true));
    System.out.println("Failed: " + parts.get(false));
  }
}

Example

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

public class Main {
  public static void main(String[] args) {
    List<String> words = List.of("apple", "fig", "banana", "kiwi", "plum", "date");
    Map<Integer, Long> counts = words.stream()
        .collect(Collectors.groupingBy(String::length, Collectors.counting()));
    counts.entrySet().stream()
        .sorted(Map.Entry.comparingByKey())
        .forEach(entry -> System.out.println(entry.getKey() + ": " + entry.getValue()));
    Map<Boolean, List<String>> parts = words.stream()
        .collect(Collectors.partitioningBy(word -> word.length() >= 5));
    System.out.println("Long words: " + parts.get(true));
    System.out.println("Short words: " + parts.get(false));
  }
}

Key points

  • groupingBy puts elements into groups that share the same classifier result.
  • The second argument of groupingBy is a whole collector, and it decides what each group collects.
  • counting counts the elements inside each group.
  • mapping adapts each element before a downstream collector collects it.
  • partitioningBy splits a stream by a true-or-false test, so its map always has two keys.
  • A grouped map has no promised print order, so sorted printing by key makes a listing stable.

In this example, one word list feeds both a grouped count and a partition. The counts print sorted by length, and the partition prints long and short words in encounter order.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

What does the second argument of groupingBy do?