Java 9 Stream Changes

[Updated: Oct 11, 2017, Created: Oct 10, 2017]

Following is a quick walk-through of Java Util Stream changes in Java 9.

New Stream methods

java.util.stream.Stream has following new methods:

Stream.takeWhile(Predicate)

This method returns a stream which will not contain all the elements (from this stream) after the provided Predicate returns false while processing the pipeline.

In following example once the predicate !"orange".equals(s) returns false then further elements are skipped:

 String[] fruits = {"apple", "banana", "orange", "mango", "peach"};
Stream<String> stream = Arrays.stream(fruits).takeWhile(s -> !"orange".equals(s));
stream.forEach(System.out::println);
 apple
 banana

Note that, for unordered stream, if there are multiple elements which match the provided predicate then this operation is nondeterministic.

This method and Stream.filter() methods may sound similar at first but they are different in that the filter() method just skips the unmatched elements and proceeds further, on the other hand, the takeWhile() method skips all remaining elements once there's a match.

Following is the filter() example with the same stream and predicate:

 String[] fruits = {"apple", "banana", "orange", "mango", "peach"};
Stream<String> stream = Arrays.stream(fruits).filter(s -> !"orange".equals(s));
stream.forEach(System.out::println);
 apple
 banana
 mango
 peach

Stream.dropWhile(Predicate)

This method returns a stream which will only contain the elements (from this stream) after the provided Predicate returns false while processing the pipeline.

In following example once the predicate !"orange".equals(s) returns false further elements are accepted:

 String[] fruits = {"apple", "banana", "orange", "mango", "peach"};
Stream<String> stream = Arrays.stream(fruits).dropWhile(s -> !"orange".equals(s));
stream.forEach(System.out::println);
 orange
 mango
 peach

Stream<T> Stream.iterate(T, Predicate, UnaryOperator)

This method returns a sequential stream which terminates the iterative operation (provided by UnaryOperator) as soon as Predicate returns false.

 Stream<String> iterate = Stream.iterate("-", s -> s.length() < 5, s -> s + "-");
iterate.forEach(System.out::println);
 -
 --
 ---
 ----

This is a sibling overload method of iterate(T seed, UnaryOperator f) (old one) which returns an infinite stream.

Stream<T> Stream.ofNullable(T)

This method returns a sequential Stream containing a single element. If the provided element is null then this method returns an empty Stream.

 Stream<String> stream = Stream.ofNullable("anItem");
stream.forEach(System.out::println);
 anItem
This method can be useful when we want to append a non-null single element to a stream. For example:
 String nullableItem = "peach";
Stream<String> stream = Stream.of("apple", "banana", "orange");
Stream<String> stream2 = Stream.concat(stream, Stream.ofNullable(nullableItem));
stream2.forEach(System.out::println);
 apple
 banana
 orange
 peach

Also we do not need to take an extra step to check whether the element we are appending is null or not null:

 String nullableItem = null;
Stream<String> stream = Stream.of("apple", "banana", "orange");
Stream<String> stream2 = Stream.concat(stream, Stream.ofNullable(nullableItem));
stream2.forEach(System.out::println);
 apple
 banana
 orange

IntStream, LongStream and DoubleStream methods

These stream classes also have the equivalent of the above methods (except for the ofNullable() method).

For example

 IntStream.of(2, 4, 6, 8, 9, 10, 11)
.takeWhile(i -> i % 2 == 0)
.forEach(System.out::println);
 2
 4
 6
 8
 IntStream.of(2, 4, 6, 8, 9, 10, 11)
.dropWhile(i -> i % 2 == 0)
.forEach(System.out::println);
 9
 10
 11
 IntStream.iterate(0, i -> i < 10, i -> i + 1)
.forEach(System.out::print);
 0123456789

New Collectors methods

The class java.util.stream.Collectors has following new methods:

Collectors.filtering(Predicate, Collector)

This method processes the provided Collector while applying the provided Predicate to each input element and only accepting the elements for which the predicate returns true.

 List<Integer> list = IntStream.of(2, 4, 6, 8, 10, 12)
.boxed()
.collect(Collectors.filtering(i -> i % 4 == 0,
Collectors.toList()));
System.out.println(list);
 [4, 8, 12]

Collectors.flatMapping(Function, Collector)

This method processes the provided Collector while applying the provided mapping Function to each input element.

This method can be applied in the situations where each element (or a member of the element) of the original stream is convertible to a stream. It's a way to convert a member collection to the flat elements.

 List<Integer> list = Stream.of(List.of(1, 2, 3, 4), List.of(5, 6, 7, 8))
.collect(Collectors.flatMapping(
l -> l.stream()
.filter(i -> i % 2 == 0),
Collectors.toList()
));
System.out.println(list);
 [2, 4, 6, 8]

Following example groups the lists of stream by the lists' sizes and maps each list to the flat elements of integer which are only divisible by 2:

 Map<Integer, List<Integer>> map =
Stream.of(List.of(1, 2, 3, 4, 5, 6),
List.of(7, 8, 9, 10))
.collect(
Collectors.groupingBy(Collection::size,
Collectors.flatMapping(
l -> l.stream()
.filter(i -> i % 2 == 0),
Collectors.toList()
)));
System.out.println(map);
 {4=[8, 10], 6=[2, 4, 6]}

See Also