Group Words by Length Using Streams in Java
Problem
Grouping splits a collection into buckets that share a key — like SQL's GROUP BY — producing a Map from each key to the elements that have it.
Given a list of words, group them by their length, with the groups listed from shortest to longest.
Java Program
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;
public class GroupWordsByLength {
public static void main(String[] args) {
List<String> words = Arrays.asList("sun", "moon", "star", "sky", "comet", "planet", "orbit");
Map<Integer, List<String>> byLength = words.stream()
.collect(Collectors.groupingBy(
String::length, // the key each word is grouped under
TreeMap::new, // sorted keys, so the output order is predictable
Collectors.toList())); // what to collect inside each group
byLength.forEach((length, group) -> System.out.println(length + " letters: " + group));
}
}Output
Core Logic
groupingBy takes a classifier function that computes each element's key, and builds the map of buckets for you.
- 1
String::lengthis the classifier, so each word is filed under its length. - 2
TreeMap::newmakes the result a sorted map, so the groups print in key order. - 3
Collectors.toList()decides what each bucket holds — here, the words themselves. - 4
forEach((length, group) -> ...)prints each key with its list.
sun and sky both have length 3, so they land in the same bucket, in the order they appeared.Key Point: The one-argument groupingBy(String::length) returns a HashMap, whose iteration order isn't guaranteed — pass a map factory when the order matters.
Why: Each of the n words is inserted into a TreeMap with k distinct keys, and every word is stored in some bucket.
Key Concepts
Approach 2: Counting Each Group
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;
public class GroupWordsByLengthCounting {
public static void main(String[] args) {
List<String> words = Arrays.asList("sun", "moon", "star", "sky", "comet", "planet", "orbit");
Map<Integer, Long> counts = words.stream()
.collect(Collectors.groupingBy(String::length, TreeMap::new, Collectors.counting()));
System.out.println(counts);
}
}
Output
Core Logic
Swapping the downstream collector changes what each bucket holds — counting() stores how many words fell into it instead of the words themselves.
- 1The classifier and map factory stay the same.
- 2
Collectors.counting()replacestoList()as the downstream collector. - 3The values become
Longcounts.
Key Point: Other downstream collectors work the same way — summingInt, averagingDouble, mapping or joining — which makes groupingBy a mini reporting engine.
Why: Each word is still classified once, but only one counter per key is stored.
Key Concepts
Interview Questions
What map type does groupingBy return by default?
A HashMap. Pass a map factory such as TreeMap::new when the keys need to be sorted.
How do you collect only part of each element into the groups?
Use Collectors.mapping as the downstream collector, for example groupingBy(Person::getCity, mapping(Person::getName, toList())).