Java SE 8 introduces three primitive specialized stream interfaces to tackle this issue—
IntStream
,
DoubleStream
, and
LongStream
—that respectively specialize the elements of a stream to be
int
,
double
, and
long
.
The most-common methods you will use to convert a stream to a specialized version are
mapToInt
,
mapToDouble
, and
mapToLong
. These methods work exactly like the method
map
that we saw earlier, but they return a specialized stream instead of a
Stream<T>
. For example, we could improve the code in
Listing 13
as shown in
Listing 14
. You can also convert from a primitive stream to a stream of objects using the
boxed
operation.
int statementSum =
transactions.stream()
.mapToInt(Transaction::getValue)
.sum(); // works!
Listing 14
Finally, another useful form of numeric streams is numeric ranges. For example, you might want to generate all numbers between 1 and 100. Java SE 8 introduces two static methods available on
IntStream
,
DoubleStream
, and
LongStream
to help generate such ranges:
range
and
rangeClosed
.
Both methods take the starting value of the range as the first parameter and the end value of the range as the second parameter. However,
range
is exclusive, whereas
rangeClosed
is inclusive.
Listing 15
is an example that uses
rangeClosed
to return a stream of all odd numbers between 10 and 30.
IntStream oddNumbers =
IntStream.rangeClosed(10, 30)
.filter(n -> n % 2 == 1);
Listing 15
Building Streams
There are several ways to build streams. You’ve seen how you can get a stream from a collection. Moreover, we played with streams of numbers. You can also create streams from values, an array, or a file. In addition, you can even generate a stream from a function to produce infinite streams!
Creating a stream from values or from an array is straightforward: just use the static methods
Stream .of
for values and
Arrays.stream
for an array, as shown in
Listing 16
.
Stream<Integer> numbersFromValues = Stream.of(1, 2, 3, 4);
int[] numbers = {1, 2, 3, 4};
IntStream numbersFromArray = Arrays.stream(numbers);
Listing 16
In contrast to collections, which are iterated explicitly (
external iteration
),
stream operations do the iteration behind the scenes for you.
You can also convert a file in a stream of lines using the
Files.lines
static method. For example, in
Listing 17
we count the number of lines in a file.
long numberOfLines =
Files.lines(Paths.get(“yourFile.txt”), Charset.defaultCharset())
.count();
Listing 17
Infinite streams.
Finally, here’s a mind-blowing idea before we conclude this first article about streams. By now you should understand that elements of a stream are produced on demand. There are two static methods—
Stream.iterate
and
Stream .generate
—that let you create a stream from a function. However, because elements are calculated on demand, these two operations can produce elements “forever.” This is what we call an
infinite stream
: a stream that doesn’t have a fixed size, as a stream does when we create it from a fixed collection.
Listing 18
is an example that uses
iterate
to create a stream of all numbers that are multiples of 10. The
iterate
method takes an initial value (here,
0
) and a lambda (of type
UnaryOperator<T>
) to apply successively on each new value produced.
Stream<Integer> numbers = Stream.iterate(0, n -> n + 10);
Listing 18
We can turn an infinite stream into a fixed-size stream using the
limit
operation. For example, we can limit the size of the stream to 5, as shown in
Listing 19
.
numbers.limit(5).forEach(System.out::println); // 0, 10, 20, 30, 40
Listing 19
Conclusion
Java SE 8 introduces the Streams API, which lets you express sophisticated data processing queries. In this article, you’ve seen that a stream supports many operations such as
filter
,
map
,
reduce
, and
iterate
that can be combined to write concise and expressive data processing queries. This new way of writing code is very different from how you would process collections before Java SE 8. However, it has many benefits. First, the Streams API makes use of several techniques such as laziness and short-circuiting to optimize your data processing queries. Second, streams can be parallelized automatically to leverage multicore architectures. In the next article in this series, we will explore more-advanced operations, such as
flatMap
and
collect
. Stay tuned.