Java Reference
In-Depth Information
you're using two terminal operations to split the stream into its head and tail: findFirst and skip.
Remember from chapter 4 that once you call a terminal operation on a stream, it's consumed
forever!
Lazy evaluation
There's an additional, more important problem: the static method IntStream.concat expects two
instances of a stream. But its second argument is a direct recursive call to primes, resulting in an
infinite recursion! For many Java purposes, restrictions on Java 8 streams such as “no recursive
definitions” are unproblematic and give your database-like queries expressivity and the ability to
parallelize. Thus, the Java 8 designers chose a sweet spot. Nonetheless, the more-general
features and models of streams from functional languages such as Scala and Haskell can be a
useful addition to your programming tool box. What you need is a way to lazily evaluate the call
to the method primes in the second argument of concat. (In a more technical programming
language vocabulary we refer to this as lazy evaluation , nonstrict evaluation , or even call by
name .) Only when you need to process the prime numbers (for example, with the method limit)
should the stream be evaluated. Scala (which we explore in the next chapter) provides support
for this idea. In Scala you can write the previous algorithm as follows, where the operator #::
does lazy concatenation (the arguments are evaluated only when you need to actually consume
the stream):
def numbers(n: Int): Stream[Int] = n #:: numbers(n+1)
def primes(numbers: Stream[Int]): Stream[Int] = {
numbers.head #:: primes(numbers.tail filter (n -> n % numbers.head != 0))
}
Don't worry about this code. Its only purpose is to show you an area of difference between Java
and other functional programming languages. It's good to reflect just a moment about how the
arguments are evaluated. In Java when you call a method, all its arguments are fully evaluated
immediately. But in Scala using #::, the concatenation returns immediately and the elements are
evaluated only when needed. Now we turn to implementing this idea of lazy lists directly in
Java.
14.3.2. Your own lazy list
Java 8 streams are often described as lazy. They're lazy in one particular aspect: a stream
behaves like a black box that can generate values on request. When you apply a sequence of
operations to a stream, these are merely saved up. Only when you apply a terminal operation to
 
Search WWH ::




Custom Search