Syntax
stream.collect(Collectors.methodName())Examples
Collecting to a List, Set, or Map
The most common collector operations.
import java.util.List;
import java.util.stream.Collectors;
List<String> names = List.of("Fola", "Zain", "Jamal", "Fola");
List<String> asList = names.stream().distinct().collect(Collectors.toList());
System.out.println(asList); // [Fola, Zain, Jamal]
var asMap = names.stream()
.distinct()
.collect(Collectors.toMap(name -> name, String::length));
System.out.println(asMap); // {Fola=4, Zain=4, Jamal=5}joining(): Building a String
Combining stream elements into a single delimited string.
import java.util.List;
import java.util.stream.Collectors;
List<String> names = List.of("Fola", "Zain", "Jamal");
String joined = names.stream().collect(Collectors.joining(", "));
System.out.println(joined); // Fola, Zain, Jamal
String withBrackets = names.stream().collect(Collectors.joining(", ", "[", "]"));
System.out.println(withBrackets); // [Fola, Zain, Jamal]groupingBy(): Grouping Elements
Splitting a stream into groups based on a classifier function, producing a Map.
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
record Student(String name, String grade) {}
List<Student> students = List.of(
new Student("Fola", "A"),
new Student("Zain", "B"),
new Student("Jamal", "A"),
new Student("Amir", "C")
);
Map<String, List<Student>> byGrade = students.stream()
.collect(Collectors.groupingBy(Student::grade));
System.out.println(byGrade.get("A")); // [Student[name=Fola, grade=A], Student[name=Jamal, grade=A]]counting() and Downstream Collectors
Combining groupingBy() with a downstream collector like counting() for summary statistics.
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
List<String> words = List.of("apple", "banana", "avocado", "blueberry", "cherry");
Map<Character, Long> countByFirstLetter = words.stream()
.collect(Collectors.groupingBy(w -> w.charAt(0), Collectors.counting()));
System.out.println(countByFirstLetter); // {a=2, b=2, c=1}Best practices
- Use Collectors.toList() (or the shorter .toList() method directly) as the default when you just need a plain result list
- Use Collectors.groupingBy() instead of manually building a Map<K, List<V>> with a loop - it handles the bucketing logic for you
- Combine groupingBy() with a downstream collector (counting(), summingInt(), etc.) to compute per-group statistics in one pass
- Use Collectors.joining() with a delimiter for building comma or newline-separated output instead of a manual StringBuilder loop
At a glance
- Purpose
- General-purpose application development
- File extension
- .java
- Runs in
- Java Virtual Machine
- Usually used with
- JDK and Java libraries
Specifications & further reading
Related Java documentation
Lambda Expressions
A lambda expression is a compact way to represent an anonymous function - a block of code that can be passed around as a value. Lambdas work with functional interfaces (interfaces with exactly one abstract method), letting you replace a full anonymous class implementation with a short, focused expression. Lambdas are the foundation that makes Java's Stream API and functional-style programming possible.Stream Basics (map, filter, forEach)
The Stream API, introduced in Java 8, provides a functional, declarative way to process sequences of elements from collections, arrays, or other sources. A stream pipeline consists of a source, zero or more intermediate operations (like filter and map, which are lazy and return a new stream), and exactly one terminal operation (like forEach or collect) that actually triggers processing.Stream Terminal Operations
Terminal operations are what actually trigger a stream pipeline to execute and produce a final result, consuming the stream in the process - after a terminal operation runs, the stream cannot be reused. Common terminal operations include reduce() for combining elements into a single value, count(), sum-style operations for numeric streams, anyMatch()/allMatch() for boolean checks, and findFirst() for retrieving a single element.Optional
Optional<T> is a container object that may or may not hold a non-null value, designed to make the possibility of 'no result' explicit in a method's return type, reducing NullPointerExceptions. Rather than returning null and hoping callers remember to check for it, a method returning Optional<T> signals clearly that a value might be absent, and provides safe methods like isPresent(), ifPresent(), and orElse() for handling both cases.
A lambda expression is a compact way to represent an anonymous function - a block of code that can be passed around as a value. Lambdas work with functional interfaces (interfaces with exactly one abstract method), letting you replace a full anonymous class implementation with a short, focused expression. Lambdas are the foundation that makes Java's Stream API and functional-style programming possible.Stream Basics (map, filter, forEach)
The Stream API, introduced in Java 8, provides a functional, declarative way to process sequences of elements from collections, arrays, or other sources. A stream pipeline consists of a source, zero or more intermediate operations (like filter and map, which are lazy and return a new stream), and exactly one terminal operation (like forEach or collect) that actually triggers processing.Stream Terminal Operations
Terminal operations are what actually trigger a stream pipeline to execute and produce a final result, consuming the stream in the process - after a terminal operation runs, the stream cannot be reused. Common terminal operations include reduce() for combining elements into a single value, count(), sum-style operations for numeric streams, anyMatch()/allMatch() for boolean checks, and findFirst() for retrieving a single element.Optional
Optional<T> is a container object that may or may not hold a non-null value, designed to make the possibility of 'no result' explicit in a method's return type, reducing NullPointerExceptions. Rather than returning null and hoping callers remember to check for it, a method returning Optional<T> signals clearly that a value might be absent, and provides safe methods like isPresent(), ifPresent(), and orElse() for handling both cases.