The Master Class of "Learn Spring Security" is live:
1. Overview
In this quick write-up, we are going to focus on the new interesting Stream API improvements coming in Java 9.
2. Stream takeWhile/dropWhile
Discussions about these methods have appeared repeatedly on StackOverflow (the most popular is this one).
Imagine that we want to generate a Stream of Strings by adding one character to the previous Stream‘s value until the length of the current value in this Stream is lower than 10.
How would we solve it in Java 8? We could use one of the short-circuiting intermediate operations like limit, allMatch that actually serve for other purposes or write our own takeWhile implementation based on a Spliterator that, in turn, complicates such a simple issue.
With Java 9, the solution is easy:
Stream<String> stream = Stream.iterate("", s -> s + "s") .takeWhile(s -> s.length() < 10);
The takeWhile operation takes a Predicate which is applied to elements to determine the longest prefix of these elements (if a stream is ordered) or a subset of the stream’s elements (when a stream is unordered).
To move forward, we had better understand what terms “the longest prefix” and “a Stream’s subset” mean:
- the longest prefix is a contiguous sequence of elements of the stream that match the given predicate. The first element of the sequence is the first element of this stream, and the element immediately following the last element of the sequence does not match the given predicate
- a Stream’s subset is a set of some (but not all) elements of the Stream match the given predicate.
After introducing these key terms, we can easily comprehend another new dropWhile operation.
It does exactly the opposite of takeWhile. If a stream is ordered, the dropWile returns a stream consisting of the remaining elements of this Stream after dropping the longest prefix of elements that match the given predicate.
Otherwise, if a Stream is unordered, the dropWile returns a stream consisting of the remaining elements of this Stream after dropping a subset of elements that match the given predicate.
Let’s throw away the first five elements by using the preceding obtained Stream:
stream.dropWhile(s -> !s.contains("sssss"));
Simply put, the dropWhile operation will remove elements while the given predicate for an element returns true and stops removing on the first predicate’s false.
3. Stream iterate
The next new feature is the overloaded iterate method for finite Streams generation. Not to be confused with the finite variant which returns an infinite sequential ordered Stream produced by some function.
A new iterate slightly modifies this method by adding a predicate which applies to elements to determine when the Stream must terminate. Its usage is very convenient and concise:
Stream.iterate(0, i -> i < 10, i -> i + 1) .forEach(System.out::println);
It can be associated with the corresponding for statement:
for (int i = 0; i < 10; ++i) { System.out.println(i); }
4. Stream ofNullable
There are some situations when we need to put an element into a Stream. Sometimes, this element may be a null, but we don’t want that our Stream contains such values. It causes of writing either an if statement or a ternary operator which checks whether an element is a null.
Assuming that collection and map variables, have been created and filled successfully, have a look at the following example:
collection.stream() .flatMap(s -> { Integer temp = map.get(s); return temp != null ? Stream.of(temp) : Stream.empty(); }) .collect(Collectors.toList());
To avoid such boilerplate code, the ofNullable method has been added to the Stream class. With this method the preceding sample can be simply transformed into:
collection.stream() .flatMap(s -> Stream.ofNullable(map.get(s))) .collect(Collectors.toList());
5. Conclusion
We considered major changes of Stream API in Java 9 and how these improvements will help us to write more emphatic programs with fewer efforts.
As always, the code snippets can be found over on Github.