What is a Stream?
The concept of streams in Java was introduced in version 8 and provides us with a different way of operating on sequences of elements of a given type.
You can think of a stream as a pipe that contains a sequence of elements. These elements can be processed/transformed/filtered based on a set of operations that we provide.
For example, let’s assume we have a Stream<Integer>
that:
-
Contains the sequence 1, 2, 4 and 5
-
Where we want to filter the elements and only allow even numbers
-
And then we want to collect our results in an ArrayList
This can be represented as follows:
The 3 concepts in the diagram, which we’ll cover in these sections, make up the stream pipeline that define how our data will be processed.
Creating a Stream<T>
Java provides us with different mechanisms to create a Stream<T>
of elements. In this section we’ll cover 3 of the most common approaches to create a Stream.
Using the Stream.of
methods
The Stream
interface provides us with static utility methods to create a stream from a single element or an array of elements we have. For example, to create the Stream<Integer>
in our example above we could do the following:
Stream<Integer> numbers = Stream.of(1, 2, 4, 5);
Or, alternatively, if we already have an array, we can pass it into the of
method:
Integer[] array = { 1, 2, 4, 5 };
Stream<Integer> numbers = Stream.of(array)
Using the stream()
method in Collection
types
All implementations of the Collection
interface in Java (for example, ArrayList
and others covered in chapter 7), provide a stream()
method that will create a Stream
with the elements in the collection.
For example, if we had a List<Integer>
with the elements in the example we could do the following:
List<Integer> input = Arrays.asList(1, 2, 4, 5);
Stream<Integer> numbers = input.stream();
Creating IntStream
instances
The Stream<T>
interface we are discussing is meant for objects of any type, however, it isn’t meant for primitive types. Java provides specialized stream interfaces that work with primitive types, like IntStream
.
For example, if we want a stream of int
values that has the sequence 1, 2, 3, 4, we can write:
IntStream intStream = IntStream.range(1, 5);
The IntStream.range method doesn’t include the last number (it is exclusive on the end). If you want to include both the start and the end, you can use IntStream.rangeClosed .
|
If you need a Stream<Integer>
based on an IntStream
you can use the boxed()
method as follows:
IntStream intStream = IntStream.range(1, 5);
Stream<Integer> integers = intStream.boxed();
Notes about Streams
In this section we covered a couple of options to create a Stream
in Java. Once you have a Stream
ready, there are a couple of things that are important to note:
-
Stream pipelines can have zero or more intermediate operations. We’ll cover this in the next section.
-
Streams are sequences of elements, hence, the elements are processed in the order they arrive. However, streams can be created as parallel streams in which case the processing order isn’t guaranteed.
-
Streams are lazy. This means that no processing will occur until you provide a terminal operation.
-
In general, a
Stream
shouldn’t be reused. If you need to reuse a Stream it is a good practice to create a newStream
instance.