Java Reference
In-Depth Information
In practice, implementations of
reduce()
can be more sophis‐
ticated than these, and can even execute in parallel if the data
structure and operations are amenable to this.
Let's look at a quick example of a
reduce()
and calculate the sum of some primes:
double
sumPrimes
=
((
double
)
Stream
.
of
(
2
,
3
,
5
,
7
,
11
,
13
,
17
,
19
,
23
)
.
reduce
(
0
,
(
x
,
y
)
->
{
return
x
+
y
;}));
System
.
out
.
println
(
"Sum of some primes: "
+
sumPrimes
);
In all of the examples we've met in this section, you may have noticed the presence
of a
stream()
method call on the
List
instance. This is part of the evolution of the
Collections—it was originally chosen partly out of necessity, but has proved to be an
excellent abstraction. Let's move on to discuss the Streams API in more detail.
The Streams API
The issue that caused the library designers to introduce the Streams API was the
large number of implementations of the core collections interfaces present in the
wild. As these implementations predate Java 8 and lambdas, they would not have
any of the methods corresponding to the new functional operations. Worse still, as
method names such as
map()
and
filter()
have never been part of the interface of
the Collections, implementations may already have methods with those names.
To work around this problem, a new abstraction called a
Stream
was introduced—
the idea being that a
Stream
object can be generated from a collection object via the
stream()
method. This
Stream
object, being new and under the control of the
library designers, is then guaranteed to be free of method name collisions. This then
mitigates the risk of clash, as only implementations that contained a
stream()
method would be affected.
A
Stream
object plays a similar role to an
Iterator
in the new approach to collec‐
tions code. The overall idea is for the developer to build up a sequence (or “pipe‐
line”) of operations (such as map, filter, or reduce) that need to be applied to the
collection as a whole. The actual content of the operations will usually be expressed
by using a lambda expression for each operation.
At the end of the pipeline, the results need to be gathered up, or “materialized” back
into an actual collection again. This is done either by using a
Collector
or by fin‐
ishing the pipeline with a “terminal method” such as
reduce()
that returns an
actual value, rather than another stream. Overall, the new approach to collections
looks like this:
stream
()
filter
()
map
()
collect
()
Collection
->
Stream
->
Stream
->
Stream
->
Collection
The
Stream
class behaves as a sequence of elements that are accessed one at a time
(although there are some types of streams that support parallel access and can be