Seven Ways to Create a Stream in Java

Overview

In Java a Stream is a sequence of elements supporting sequential and parallel aggregate operations. It was added in Java 8. In this post, we will look at seven ways to create a Stream in Java.

1. Stream.of and ofNullable

Creating a single element stream

The Stream.of method returns a sequential stream with a single element. 

Stream<String> stream = Stream.of("a");

The above created stream has just one element in it.

It will throw a NullPointerException if we pass null.
Stream.of(null); //throws a java.lang.NullPointerException

To handle nulls, we can use the ofNullable method which is added in JDK 9. It does the same thing as the of method, but will create and return an empty Stream if passed a null.

Stream<String> stream = Stream.ofNullable("a"); // same as Stream.of("a")
Stream.of(null); //creates an empty stream

Creating a stream with variable number of elements

There is an overloaded of method in the Stream class which accepts a varargs argument. It creates and returns a sequential, ordered stream with the passed elements.

Stream.of("a", "b", "c")
        .forEach(System.out::println);

Prints,

a
b
c

Since it accepts a var-args we can pass an array too.

String[] values = new String[]{"a", "b", "c"};
stream = Stream.of(values); //The stream has strings "a", "b", "c"

2. Stream generate method

The static method generate accepts a Supplier and returns an infinite sequential unordered stream. Each element of the stream is what the passed Supplier returns. We use this for creating infinite streams of random/constant values or sequence of numbers.

Since it creates an infinite stream of numbers, for practical usages, there must be a limit operation or a short-circuiting operation on the stream.

Let us create an incrementing sequence of numbers starting with 1 and limit the stream to 5 elements.

AtomicInteger atomicInteger = new AtomicInteger(0);
Stream.generate(atomicInteger::incrementAndGet)
        .limit(5)
        .forEach(System.out::println);

The method reference atomicInteger::incrementAndGet is equivalent to the lambda expression () -> atomicInteger.incrementAndGet(). Thus, the supplier keeps generating sequences of integers but we limit the stream to 5 elements.

The above prints,

1
2
3
4
5

To create a stream of random UUIDs, we can use UUID’s randomUUID method as the supplier.

Stream.generate(UUID::randomUUID)
        .limit(5)
        .forEach(System.out::println);

To create a stream of constants,

Stream.generate(() -> 42)
        .limit(5)
        .forEach(System.out::println);

Note that even though the documentation states it returning an unordered stream, the current implementation returns an ordered stream only. It doesn’t change the order of elements created from the supplier. But in the future an optimization could be added and it could return an unordered stream.

3. Stream iterate

Signature of the iterate method:
Stream<T> iterate(final T seed, final UnaryOperator<T> f)
 
It accepts an initial seed value and an UnaryOperator. An UnaryOperator is a Function<T, T> i.e., it accepts and returns an element of the same type. The first element of the stream is the seed value itself and the function is repeatedly applied to the previous element to create a stream. It returns an infinite sequential ordered stream.
 
In other words, the stream is seed, f(seed), f(f(seed)) etc.
Stream.iterate(1, i -> i + 1)
        .limit(5)
        .forEach(System.out::println);

The above code starts with value 1 as the seed. The UnaryOperator i  -> i + 1 takes an integer and increments it by one and returns it. This will print,

1
2
3
4
5

The lambda expression will first be invoked with 1 which will result in 2. Next, it will be called with 2 which results. in 3 and so on. This is equivalent to the below infinite for loop,

for (int i = 1; ; i = i + 1) {

}

Stream iterate with a predicate

Since Java 9, there is an overloaded iterate method which accepts a predicate to evaluate when the stream must terminate. Hence it returns a finite sequential ordered stream. The predicate is thus equivalent to the hasNext of an Iterator.

Signaure:
Stream<T> iterate(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next)
 
Similar to the first iterate method we saw, it starts with the seed element and applies the function to it. But each time, it evaluates the predicate function on the result of applying the function. If it returns true, it is added as part of the stream; else the stream terminates.
 
If the predicate returns false for the seed element, the stream will be empty.
Stream.iterate(1, i -> i <= 5, i -> i + 1)
        .forEach(System.out::println);

We start with a seed value of 1. The function returns the previous value + 1. The predicate tests if the value is less than or equal to 5. Thus the stream will terminate when it evaluates the predicate after generating 5 (because the function would return 6 when passing 5 as input and 6 is not less than or equal to 5).

This is equivalent to,

for (int i = 1; i <= 5; i++) {

}

4. Using Stream Builder

A method not often used is the builder method of the Stream class. It follows the Builder pattern.

Stream<String> stream = Stream.<String>builder()
        .add("a")
        .add("b")
        .add("c")
        .build();
stream.forEach(System.out::println);

Prints,

a
b
c

5. Collection stream

A most common way to create a stream is to call the stream method on the Collection instance. It will return a sequential stream with the elements of the stream. Whether the stream is ordered or unordered depends on the collection. For example, a stream created from a List is ordered whereas a HashSet returns an unordered stream.

List<String> list = List.of("a", "b", "c");
list.stream()
        .forEach(System.out::println);

6. Arrays.stream

When we have an array and want to turn that into a stream, we use the Arrays.stream method. It returns a sequential stream with the passed array as the stream source.

String [] values = new String[]{"a", "b", "c", "d", "e"};
Stream<String> stream = Arrays.stream(values);
stream.forEach(System.out::println);

This will print,

a
b
c
d
e

It has an overloaded method which accepts the start and end array indices which specifies the range of array from which to create a stream. The end array index is exclusive.

Stream<String> stream = Arrays.stream(values, 1, 4);
stream.forEach(System.out::println);

This will create a stream with elements b, c, and d (from index 1 to 3)

It will throw an ArrayIndexOutOfBoundsException if one of the following is true
  • the start index is negative
  • the end index is less than the start index
  • end index is greater than the array size.

There are overloaded method which accept a int[], long[] and a double[]. The above example matched the generic stream method (T[]).

7. Creating an empty stream

Use Stream.empty method to create an empty stream. It returns an empty sequential stream.

Stream<String> emptyStream = Stream.empty();

Conclusion

In this post, we learnt seven common ways to create a Stream in Java. Check out the other stream and java8 posts.

References

Why is Java Stream generator unordered?

Leave a Reply