Java Reference
In-Depth Information
structure. You can get the compiler to help enforce this “no mutation of existing structure” rule
by declaring fields key, val, left, and right of class Tree to be final; but remember that final
protects only a field and not the object pointed to, which may need its own fields to be final to
protect it, and so on.
Ah, but you might say, “I want updates to the tree to be seen by some users (but admittedly not
by some others).” Well, there are two choices: one is the classical Java solution (be very careful
when updating something to check whether you need to copy it first). The other is the
functional-style solution: you logically make a new data structure whenever you do an update
(so nothing is ever mutated) and just arrange to pass the correct version of the data structure to
users as appropriate. This idea could be enforced through an API. If certain clients of the data
structure need to have updates visible, they should go through an API that returns the latest
version. Clients who don't want updates visible (such as for long-running statistical analysis)
simply use whatever copy they retrieved, knowing that it can't be mutated from under them.
One might remark that this technique is like “updating” a file on a CD-R, which allows a file to
be written only once by burning with a laser; multiple versions of the file are all stored on the
CD (smart CD authoring software might even share common parts of multiple versions), and
you pass the appropriate block address of the start of file (or a filename encoding the version
within its name) to select which version you want to use. In Java things are rather better than on
a CD, in that old versions of the data structure that can no longer be used will be garbage
collected.
14.3. Lazy evaluation with streams
You saw in previous chapters that streams are a great way to process a collection of data. But for
various reasons, including efficient implementation, the Java 8 designers added streams to Java
in a rather specific way. In particular, one limitation is that you can't define a stream recursively
because a stream can be consumed only once. We show in the coming section how this can
sometimes be problematic.
14.3.1. Self-defining stream
Let's revisit our example from chapter 6 of generating prime numbers to understand this idea of
a recursive stream. You saw that, perhaps as part of the class MyMathUtils, you can compute a
stream of prime numbers as follows:
public static Stream<Integer> primes(int n) {
return Stream.iterate(2, i -> i + 1)
 
Search WWH ::




Custom Search