8 Essential Java Stream APIs to Simplify Collection Processing
This article introduces eight ready‑to‑use Java Stream APIs—including Stream.ofNullable, Stream.iterate, collectingAndThen, dropWhile/takeWhile, IntStream, teeing, concat, and partitioningBy—showing how each can cleanly handle nulls, generate sequences, transform collections, and combine streams with concise code examples.
1. Quickly filter null values: Stream.ofNullable
The Java 9 method Stream.ofNullable filters out null elements in a collection, helping avoid NullPointerExceptions.
List<String> names = Arrays.asList("Alice", null, "Bob", null, "Charlie");
List<String> nonNullNames = names.stream()
.flatMap(Stream::ofNullable)
.collect(Collectors.toList());
System.out.println(nonNullNames);Output:
[Alice, Bob, Charlie]2. Stream iteration: Stream.iterate()
Stream.iteratecreates an infinite sequence from a seed and a unary function.
Stream.iterate(0, n -> n + 2)
.limit(10)
.forEach(e -> System.out.println(e));Output: 0 2 4 6 8 10 12 14 16 18
Note: Because Stream.iterate() generates an infinite stream, you should define a termination condition such as limit , findFirst , or findAny to avoid endless loops.
3. Collection transformation: collectingAndThen()
Introduced in Java 8, collectingAndThen applies a finishing transformation to the result of another collector.
List<String> fruits = Arrays.asList("apple", "banana", "orange");
Map<Integer, String> result = fruits.stream()
.collect(Collectors.collectingAndThen(
Collectors.toMap(fruits::indexOf, String::toUpperCase),
Collections::unmodifiableMap));
System.out.println(result);Output:
{0=APPLE, 1=BANANA, 2=ORANGE}4. Drop and take while: dropWhile() & takeWhile()
Both methods, added in Java 9, process streams sequentially based on a predicate. takeWhile(): returns elements while the predicate holds. dropWhile(): discards elements while the predicate holds.
List<Integer> numbers = List.of(1,2,3,4,5,6,7);
numbers.stream()
.dropWhile(n -> n < 3)
.takeWhile(n -> n < 6)
.forEach(System.out::println);Output: 3 4 5
5. Integer streams: IntStream
Java 8 introduced IntStream with two common factories: IntStream.range() – excludes the end value. IntStream.rangeClosed() – includes the end value.
IntStream.range(1,5).forEach(System.out::println); // 1 2 3 4
IntStream.rangeClosed(1,5).forEach(System.out::println); // 1 2 3 4 56. Apply multiple collectors: teeing()
Java 12 added teeing to combine two collectors on the same stream.
Stream<Integer> nums = Stream.of(1,2,3,4);
Map<String, Integer> collect = nums.collect(Collectors.teeing(
Collectors.maxBy(Integer::compareTo),
Collectors.minBy(Integer::compareTo),
(e1, e2) -> Map.of("min", e1.get(), "max", e2.get())
));
System.out.println(collect);Output:
{max=4, min=1}7. Merge streams: Stream.concat()
Stream.concatjoins two streams into one.
Stream<Integer> stream1 = Stream.of(1,2,3);
Stream<Integer> stream2 = Stream.of(4,5,6);
Stream.concat(stream1, stream2).forEach(System.out::println);Output: 1 2 3 4 5 6
8. Partitioning: Collectors.partitioningBy
partitioningBysplits a stream into two groups based on a predicate.
Map<Boolean, List<String>> result1 = Stream.of("apple","banana","orange","grape")
.collect(Collectors.partitioningBy(f -> f.length() > 5));
System.out.println(result1);Output:
{false=[apple, grape], true=[banana, orange]}Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
