Java Streams
Syntax
parts of a stream: input, intermediate operation(s), terminal operation
IntStream.range(0, 10).filter(x -> x % 2 == 0).peek(System.out::println).reduce(0, (x, y) -> x + y)
Input
Collection - List, Set, etc.:
import java.util.List;
import java.util.Arrays;
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.stream().forEach(System.out::println);
Collection - Map:
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
Map<String, Integer> studentScores = new HashMap<>();
studentScores.put("Alice", 85);
studentScores.put("Bob", 90);
studentScores.put("Charlie", 75);
Set<Map.Entry<String, Integer>> entries = studentScores.entrySet();
entries.stream().forEach(entry -> System.out.println(entry.getKey() + ": " + entry.getValue()));
Array:
import java.util.Arrays;
int[] numbers = {1, 2, 3, 4, 5};
Arrays.stream(numbers).forEach(System.out::println);
Stream.empty() - used to avoid returning null:
import java.util.stream.Stream;
import java.util.List;
// source: Baeldung: https://www.baeldung.com/java-8-streams
public Stream<String> streamOf(List<String> list) {
return list == null || list.isEmpty() ? Stream.empty() : list.stream();
}
Stream.generate():
import java.util.stream.Stream;
import java.util.Random;
// Generate 10 random even numbers between 0 (inclusive) and 100 (exclusive)
Stream<Integer> evenNumbers =
Stream.generate(() -> new Random().nextInt(100)).filter(n -> n % 2 == 0);
evenNumbers.limit(10).forEach(System.out::println);
Stream.iterate(seed, hasNext, next) - hasNext param only from Java 9:
import java.util.stream.Stream;
// Print all powers of 2 ≤ 20 - starts from 1, multiplies by 2 at each iteration
Stream<Integer> stream = Stream.iterate(1, i -> i <= 20, i -> i * 2);
stream.forEach(System.out::println);
Random.doubles/ints/longs() - 0-3 parameters; 1 parameter: stream size; 2 parameters: lower bound (inclusive), upper bound (exclusive); 3 parameters: stream size, upper and lower bound:
import java.util.Random;
import java.util.stream.IntStream;
Random random = new Random();
IntStream randomNumbers = random.ints(10, 1, 100);
randomNumbers.forEach(System.out::println);
Stream.of():
import java.util.stream.Stream;
Stream<String> linuxDistros =
Stream.of("Debian", "Fedora", "Arch", "Ubuntu", "Manjaro", "Mint", "Zorin");
linuxDistros.forEach(System.out::println);
IntStream, LongStream, DoubleStream of() method:
import java.util.stream.IntStream;
IntStream integers = IntStream.of(1, 2, 3, 4, 5);
integers.forEach(System.out::println);
Stream.builder():
import java.util.stream.Stream;
Stream<String> linuxDistros =
Stream.<String>builder().add("Debian").add("Arch").add("Fedora").build();
linuxDistros.forEach(System.out::println);
IntStream, LongStream, DoubleStream range(startInclusive, endExclusive) and rangeClosed(startInclusive, endInclusive) methods:
import java.util.stream.IntStream;
IntStream intRange = IntStream.range(1, 5);
intRange.forEach(System.out::println); // 1, 2, 3, 4
Files.lines():
import java.util.stream.Stream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.io.IOException;
try (Stream<String> lines = Files.lines(Paths.get("data.txt"))) {
lines.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
Optional:
import java.util.stream.Stream;
import java.util.Optional;
Stream<Integer> opt = Optional.of(42).stream();
opt.forEach(System.out::print);
Pattern.compile().splitAsStream():
import java.util.stream.Stream;
import java.util.regex.Pattern;
// Split "hello world" along one or more whitespace characters
Stream<String> words = Pattern.compile("\\s+").splitAsStream("hello world");
words.forEach(System.out::println);
Intermediate operations
filter() - return elements of a stream which match a condition:
import java.util.stream.Stream;
import java.util.Arrays;
Stream<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6).stream();
numbers.filter(n -> n % 2 == 0).forEach(System.out::println); // 2, 4, 6
map() - call a method on each element of the stream:
import java.util.stream.Stream;
import java.util.Arrays;
Stream<String> names = Arrays.asList("Alice", "Bob", "Charlie").stream();
names.map(String::toUpperCase).forEach(System.out::println);
// ALICE, BOB, CHARLIE
mapToInt()/mapToLong()/mapToDouble():
import java.util.stream.Stream;
import java.util.Arrays;
Stream<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6).stream();
numbers.mapToInt(n -> n).forEach(System.out::println); // 1, 2, 3, 4, 5, 6
flatMap() - calls a method on all elements of a stream, returns the result as a single stream:
import java.util.stream.Stream;
import java.util.Arrays;
Stream<String> listOfStrings = Arrays.asList("Hello", "World").stream();
listOfStrings.flatMap(s -> Stream.of(s.split(""))).forEach(System.out::println);
// "H" "e" "l" "l" "o" "W" "o" "r" "l" "d"
limit() - return only a specified number of elements:
import java.util.stream.Stream;
Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5, 6);
numbers.limit(3).forEach(System.out::println); // 1, 2, 3
distinct() - no duplicates:
import java.util.stream.Stream;
import java.util.Arrays;
Stream<Integer> numbers = Arrays.asList(1, 2, 2, 3, 4, 4, 5).stream();
numbers.distinct().forEach(System.out::println); // 1, 2, 3, 4, 5
skip() - exclude an element, note that elements are counted from 1, not 0!:
import java.util.stream.Stream;
Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5, 6);
numbers.skip(1).forEach(System.out::println); // 2, 3, 4, 5, 6
sorted():
import java.util.stream.Stream;
Stream<String> numbers = Stream.of("World", "Hello");
numbers.sorted().forEach(System.out::println); // Hello, World
peek() - used mainly for debugging to print out intermediary values:
import java.util.stream.Stream;
Stream.of("Mint", "Zorin", "Bazzite", "Nobara", "LMDE")
.filter(e -> e.length() > 5)
.peek(e -> System.out.println("Filtered string: " + e))
.map(String::toUpperCase)
.peek(e -> System.out.println("Mapped string: " + e))
.map(String::length)
.forEach(e -> System.out.println("Length of string: " + e));
/*
Filtered string: Bazzite
Mapped string: BAZZITE
Length of string: 7
Filtered string: Nobara
Mapped string: NOBARA
Length of string: 6
*/
takeWhile(), dropWhile() - only supported from Java 9 - take/drop elements until an element matches the provided condition:
import java.util.List;
import java.util.Arrays;
List<Integer> numbers = Arrays.asList(1, 30, 2, 40, 21, 14);
numbers.stream().takeWhile(s -> s < 40).forEach(System.out::println); // 1, 30, 2
numbers.stream().dropWhile(s -> s < 40).forEach(System.out::println); // 40, 21, 14
boxed() - convert a primitive stream to an object stream (e.g. stream of ints to stream of Integers):
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Collectors;
List<Integer> numbers = IntStream.of(1,2,3).boxed().collect(Collectors.toList());
Terminal operations
forEach():
import java.util.stream.Stream;
Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5);
numbers.forEach(System.out::println);
count():
import java.util.stream.Stream;
Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5);
long count = numbers.count(); // 5
min() / max():
import java.util.stream.Stream;
import java.util.Optional;
Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5);
Optional<Integer> min = numbers.min(Integer::compare); // Optional[1]
Integer minValue = min.get(); // 1
sum() - only works on primitive streams (IntStream, DoubleStream, LongStream)!:
import java.util.stream.IntStream;
IntStream numbers = IntStream.of(1, 2, 3);
int total = numbers.sum(); // 6
average() - only works on primitive streams (IntStream, DoubleStream, LongStream)!:
import java.util.stream.IntStream;
import java.util.OptionalDouble;
IntStream numbers = IntStream.of(1,2,3);
OptionalDouble avg = numbers.average(); // OptionalDouble[2.0]
double avgValue = avg.getAsDouble(); // 2.0
collect() - only works on object streams (e.g. Stream<Integer>, not IntStream)
import java.util.stream.Stream;
import java.util.List;
import java.util.stream.Collectors;
Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5);
List<Integer> collect = numbers.collect(Collectors.toList());
See Collectors section of this cheatsheet below for some of the common Collectors.
toArray() - only works on object streams (e.g. Stream<Integer>, not IntStream), array also needs to be object array (e.g. Integer[], not int[]):
import java.util.Arrays;
import java.util.stream.Stream;
Stream<Integer> listStream = Arrays.asList(1, 2, 3, 4, 5, 6).stream();
Integer[] numArray = listStream.toArray(Integer[]::new);
reduce() - parameters: 1. base value (optional), 2. function with element and next element as parameters, at first iteration, base value is used (if it's provided):
import java.util.stream.Stream;
import java.util.Optional;
import java.util.List;
import java.util.Arrays;
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
Integer sum = numbers.stream().reduce(0, (a, b) -> a + b); // 15
Optional<Integer> sum2 = numbers.stream().reduce((a, b) -> a + b); // Optional[15]
allMatch(), anyMatch(), noneMatch() - check if all, any or no elements match a condition:
import java.util.List;
import java.util.Arrays;
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
boolean allEven = numbers.stream().allMatch(n -> n % 2 == 0); // false
boolean containsEven = numbers.stream().anyMatch(n -> n % 2 == 0); // true
boolean noEven = numbers.stream().noneMatch(n -> n % 2 == 0); // false
findFirst():
import java.util.stream.Stream;
import java.util.Optional;
Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5);
Optional<Integer> first = numbers.findFirst(); // Optional[1]
Integer firstValue = first.get(); // 1
Collectors
toList(), toSet():
import java.util.stream.Stream;
import java.util.List;
import java.util.stream.Collectors;
Stream<String> stream = Stream.of("Debian", "Fedora", "Arch", "Ubuntu", "Mint");
List<String> linuxDistros = stream.collect(Collectors.toList());
joining() - 0, 1, or 3 parameters; 1: delimiter; 3: delimiter, prefix, suffix:
import java.util.stream.Stream;
import java.util.stream.Collectors;
List<String> list = List.of("Debian", "Fedora", "Arch", "Ubuntu", "Mint");
String linuxDistros1 = list.stream().collect(Collectors.joining()); // DebianFedoraArchUbuntuMint
String linuxDistros2 = list.stream().collect(Collectors.joining(", ")); // Debian, Fedora, Arch, Ubuntu, Mint
String linuxDistros3 = list.stream().collect(Collectors.joining(", ", "[", "]")); // [Debian, Fedora, Arch, Ubuntu, Mint]
summarizingInt/Double/Long():
import java.util.stream.Stream;
import java.util.stream.Collectors;
import java.util.IntSummaryStatistics;
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
IntSummaryStatistics stats = stream.collect(Collectors.summarizingInt(i -> i));
// IntSummaryStatistics{count=6, sum=21, min=1, average=3.500000, max=6}
averagingInt/Double/Long(), summingInt/Double/Long():
import java.util.List;
import java.util.stream.Collectors;
List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6);
double avg = numbers.stream().collect(Collectors.averagingDouble(Double::valueOf)); // 3.5
int sum = numbers.stream().collect(Collectors.summingInt(Integer::intValue)); // 21
counting():
import java.util.stream.Stream;
import java.util.stream.Collectors;
Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5, 6);
long counter = numbers.collect(Collectors.counting()); // 6
groupingBy():
import java.util.List;
import java.util.stream.Stream;
import java.util.stream.Collectors;
import java.util.Map;
List<String> linuxDistros = List.of("Debian", "Fedora", "Arch", "Ubuntu", "Mint");
Map<Integer, List<String>> byLen =
linuxDistros.stream().collect(Collectors.groupingBy(String::length));
// {4=[Arch, Mint], 6=[Debian, Fedora, Ubuntu]}
Map<Integer, Long> byLen2 = linuxDistros.stream()
.collect(Collectors.groupingBy(String::length, Collectors.counting()));
// {4=2, 6=3}
partitioningBy() - similar to groupingBy(), but creates a true and a false group:
import java.util.List;
import java.util.stream.Stream;
import java.util.stream.Collectors;
import java.util.Map;
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
Map<Boolean, List<Integer>> isEven =
stream.collect(Collectors.partitioningBy(i -> i % 2 == 0));
// {false=[1, 3, 5], true=[2, 4, 6]}
reducing():
import java.util.stream.Stream;
import java.util.stream.Collectors;
import java.util.Optional;
Stream<String> stream = Stream.of("Arch", "Slackware", "Debian", "Zorin", "Pop!_OS");
Optional<String> longest =
stream.collect(Collectors.reducing((a,b) -> a.length() >= b.length() ? a : b));
String longestValue = longest.get(); // Slackware
For the full list of collector methods, see the official documentation.
Parallel streams
Create a parallel stream, using parallelStream() method:
import java.util.List;
import java.util.stream.Stream;
List<String> linuxDistros = List.of("Arch", "Slackware", "Debian", "Zorin", "Pop!_OS");
Stream<String> stream = linuxDistros.parallelStream();
Convert an existing stream to parallel, using parallel() method:
import java.util.stream.Stream;
Stream<String> linuxDistros =
Stream.of("Arch", "Slackware", "Debian", "Zorin", "Pop!_OS");
Stream<String> linuxDistrosParallel = linuxDistros.parallel();
Return to sequential execution, using sequential() method:
import java.util.stream.Stream;
Stream<String> linuxDistrosParallel =
Stream.of("Arch", "Slackware", "Debian", "Zorin", "Pop!_OS").parallel();
Stream<String> linuxDistros = linuxDistrosParallel.sequential();
Terminal operations for parallel streams
findAny() returns any element.
import java.util.stream.Stream;
import java.util.Optional;
Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5).parallel();
Optional<Integer> any = numbers.findAny();
any.ifPresent(v -> System.out.println(v)); // could be 3, 4, etc.
forEachOrdered() keeps the order of elements (forEach() doesn't keep it in case of a parallel stream):
import java.util.stream.Stream;
Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5).parallel();
numbers.forEachOrdered(System.out::println); // prints 1‑5 in order
collect(Collectors.groupingByConcurrent()) more efficient, parallel stream variant of groupingBy(), doesn't keep order of elements (unlike groupingBy()):
import java.util.List;
import java.util.stream.Stream;
import java.util.stream.Collectors;
import java.util.Map;
List<String> linuxDistros = List.of("Debian", "Fedora", "Arch", "Ubuntu", "Mint");
Map<Integer, List<String>> byLen = linuxDistros.parallelStream()
.collect(Collectors.groupingByConcurrent(String::length));
// {4=[Arch, Mint], 6=[Debian, Fedora, Ubuntu]}
Map<Integer, Long> byLenCount = linuxDistros.parallelStream()
.collect(Collectors.groupingByConcurrent(String::length, Collectors.counting()));
// {4=2, 6=3}