Java Reference
In-Depth Information
supported by streams. You specify to a stream what you want by passing an algorithm using lambda expressions to
the stream and the stream applies your algorithm to its data element by iterating over its elements internally and gives
you the result.
Using external iteration, typically, produces sequential code; that is, the code can be executed only by one thread.
For example, when you wrote the logic to compute the sum using a for-each loop, the loop must be executed only
by one thread. All modern computers come with a multicore processor. Wouldn't it be nice to take advantage of the
multicore processor to execute the logic in parallel? The Java library provides a Fork/Join framework to divide a task
into subtasks recursively and execute the subtasks in parallel, taking advantage of a multicore processor. However, the
Fork/Join framework is not so simple to use, especially for beginners.
Streams come to your rescue! They are designed to process their elements in parallel without you even noticing
it! This does not mean that streams automatically decide for you when to process their elements in serial or parallel.
You just need to tell a stream that you want to use parallel processing and the stream will take care of the rest. Streams
take care of the details of using the Fork/Join framework internally. You can compute the sum of squares of odd
integers in the list in parallel, like so:
int sum = numbers. parallelStream()
.filter(n -> n % 2 == 1)
.map(n -> n * n)
.reduce(0, Integer::sum);
All you had to do was replace the method called stream() with parallelStream() ! The Streams API will use
multiple threads to filter the odd integers, compute their squares, and add them to compute partial sums. Finally, it
will join the partial sums to give you the result. In this example, you have only five elements in the list. Using multiple
threads to process them is overkill. You will not use parallel processing for such a trivial computation. I have presented
this example to drive home the point that parallelizing your computation using streams is free; you get it by just using
a different method name! The second point is that parallelizing the computation was made possible because of the
internal iteration provided by the stream.
Streams are designed to use internal iteration. They provide an iterator() method that returns an Iterator to
be used for external iteration of its elements. You will “never” need to iterate elements of a stream yourself using its
iterator. If you ever need it, here is how to use it:
// Get a list of integers from 1 to 5
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
...
// Get an iterator from the stream
Iterator<Integer> iterator = numbers.stream(). iterator() ;
while(iterator.hasNext()) {
int n = iterator.next();
...
}
Imperative vs. Functional
Collections support imperative programming whereas streams support declarative programming. This is an offshoot
of collections supporting external iteration whereas streams support internal iteration. When you use collections,
you need to know “what” you want and “how” to get it; this is the feature of imperative programming. When you
use streams, you specify only “what” you want in terms of stream operations; the “how” part is taken care by the
Streams API. The Streams API supports the functional programming. Operations on a stream produce a result without
modifying the data source. Like in the functional programming, when you use streams, you specify “what” operations
you want to perform on its elements using the built-in methods provided by the Streams API, typically by passing a
lambda expressions to those methods, customizing the behavior of those operations.
 
Search WWH ::




Custom Search