The Java 8 Stream API is pretty cool as you can see in my last post BFS with Streams. But there is a catch. You cannot reuse Streams. Consider the following code Java-Code:
final List<String> helloList = Arrays.asList("H", "e", "l", "l", "o", ", ", "W", "o", "r", "l", "d", "!"); final Stream<String> helloStream = helloList.stream(); final Predicate<String> checkUpper = s -> !s.isEmpty() && !s.substring(0,1).toUpperCase().equals(s.substring(0, 1)); helloStream.filter(checkUpper); helloStream.filter(s -> !checkUpper.test(s));
This results in
IllegalStateException: stream has already been operated upon or closed.
The problem with Java 8’s Stream API is that they are designed for parallel execution. This decision introduced some constraints. Unfortunately, there is no sequential only-switch. What you can do is collect the results with
groupBy into a map and then create two new streams from that. But collecting is a terminal operation, not lazy, and therefore inefficient (especially in combination, with early-exit operations like
limit). You can also try to do the first filter, chain it with a
peek, and finally do the second filter. But since only elements matching the first filter will reach the second filter (i.e.
a && !a which is equal to
false), you won’t get any elements past the second filter. If you have a so called cold source (i.e. like a collection), you can just use two different streams which results in two iterations. But for hot sources (like a network or file i/o stream), this is not that easy. A possible solution is to cache the input in a collection, i.e., cool it down. But this comes with a space and performance penalty. So let us see, what our options are…