Java 8 introduced a Stream class that resembles Scala's Stream, a powerful lazy construct using which it is possible to do something like this very concisely:
def from(n: Int): Stream[Int] = n #:: from(n+1) def sieve(s: Stream[Int]): Stream[Int] = { s.head #:: sieve(s.tail filter (_ % s.head != 0)) } val primes = sieve(from(2)) primes takeWhile(_ < 1000) print // prints all primes less than 1000
I wondered if it is possible to do this in Java 8, so I wrote something like this:
IntStream from(int n) { return IntStream.iterate(n, m -> m + 1); } IntStream sieve(IntStream s) { int head = s.findFirst().getAsInt(); return IntStream.concat(IntStream.of(head), sieve(s.skip(1).filter(n -> n % head != 0))); } IntStream primes = sieve(from(2));
Fairly simple, but it produces java.lang.IllegalStateException: stream has already been operated upon or closed
because both findFirst()
and skip()
are terminal operations on Stream
which can be done only once.
I don't really have to use up the stream twice since all I need is the first number in the stream and the rest as another stream, i.e. equivalent of Scala's Stream.head
and Stream.tail
. Is there a method in Java 8 Stream
that I can use to achieve this?
Thanks.
Even if you hadn’t the problem that you can’t split an IntStream
, you code didn’t work because you are invoking your sieve
method recursively instead of lazily. So you had an infinity recursion before you could query your resulting stream for the first value.
Splitting an IntStream s
into a head and a tail IntStream
(which has not yet consumed) is possible:
PrimitiveIterator.OfInt it = s.iterator(); int head = it.nextInt(); IntStream tail = IntStream.generate(it::next).filter(i -> i % head != 0);
At this place you need a construct of invoking sieve
on the tail lazily. Stream
does not provide that; concat
expects existing stream instances as arguments and you can’t construct a stream invoking sieve
lazily with a lambda expression as lazy creation works with mutable state only which lambda expressions do not support. If you don’t have a library implementation hiding the mutable state you have to use a mutable object. But once you accept the requirement of mutable state, the solution can be even easier than your first approach:
IntStream primes = from(2).filter(i -> p.test(i)).peek(i -> p = p.and(v -> v % i != 0)); IntPredicate p = x -> true; IntStream from(int n) { return IntStream.iterate(n, m -> m + 1); }
This will recursively create a filter but in the end it doesn’t matter whether you create a tree of IntPredicate
s or a tree of IntStream
s (like with your IntStream.concat
approach if it did work). If you don’t like the mutable instance field for the filter you can hide it in an inner class (but not in a lambda expression…).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With