How to Create infinite stream with Java 8

Java 8 has brought a lot of features and Stream API is one of the most popular feature of the Java 8. In this post, we will see as how to create infinite stream with Java 8.

Infinite stream with Java 8

There are mainly two methods defined in the Stream class to create infinite streams and both are static methods defined in the Stream class.

  1. Stream.iterate()
  2. Stream.generate()

Before we dive into the details of infinite stream with Java 8, let’s start with a few basic concepts of Java Stream API

1. Intermediate and terminal operations

There are many operations that can be used with a stream object. A pipeline is created whenever we create a stream from a source. We can put these operations inside the pipeline to get the desired output. Stream operations are divided into categories: intermediate and terminal. Intermediate operations return one stream and terminal operations return non-stream values.

Both of these intermediate and terminal operations are combined to create a stream pipeline.We create a stream pipeline from a source like an array or collection and it is followed by zero or more intermediate operations and finally a terminal operation. We can’t use a stream after the terminal operation is completed. The stream is considered as consumed.Following are the intermediate operations we can use with a stream:

  1. filter()
  2. sorted()
  3. limit()
  4. distinct()
  5. skip()
  6. map()

And terminal operations are:

  1. forEach()
  2. collect()
  3. reduce()
  4. min()
  5. max()
  6. count()
  7. allMatch()
  8. anyMatch()
  9. noneMatch()
  10. findAny()
  11. findFirst()
  12. toArray()
  13. forEachOrdered()

We can use more than one intermediate operation, but these operations will be executed only after a terminal operation is called. All intermediate operations are executed immediately once a terminal operation is called.We can also create an infinite stream without using a terminal operation. It will run for an indefinite amount of time, similar to an infinite loop.

2. How to create an infinite stream

Collections are not infinite. We can hold only a finite number of values in a collection. But stream can produce values infinitely.Stream API has two different static methods defined to create an infinite streamgenerate and iterate. The generate method is defined as below:

static <T> Stream<T> generate(Supplier<T> s)

T is the type of stream elements and s is the supplier. The supplier is used to generate each value in the stream. It returns an infinite sequential unordered stream.The Iterate method is defined as below:

static <T> Stream<T> iterate(T seed,UnaryOperator<T> fn)

Similar to generate()T is the type of stream elements. seed is the initial element. This element is used with the function fn to produce the elements. The function fn is applied to the previous element to produce the current element.This method returns an infinite sequential ordered stream.The difference between Stream.iterate and Stream.generate is that Stream.iterate returns one infinite sequential ordered stream but Stream.generate returns one infinite sequential unordered stream. We can use any of these two methods to create an infinite stream.

2.1 Example

Let’s try to understand Stream.iterate with an example. As we have seen before, the first argument of Stream.iterate is a seed or starting value and the second argument is a <em>function</em>/<em>UnaryOperator</em> that calculates the current value based on the previous value.Let’s take a look at the below example:

import java.util.stream.Stream;

class Example {
    public static void main(String[] args) {
        Stream < Integer > intStream = Stream.iterate(2, i - > i * 2);
        intStream.limit(5).forEach(System.out::println);
    }
}

Output:

2
4
8
16
32

Let’s take a look at some important points:

  1. We are using Stream.iterate to produce an infinite stream and that is stored in the variable intStream.
  2. Intermediate operation limit is used to limit the output to 5 elements. We can’t use an intermediate operation without a terminal operation. So, <a aria-label="forEach (opens in a new tab)" href="https://www.javadevjournal.com/java/java-loops/" target="_blank" rel="noreferrer noopener" class="rank-math-link">forEach</a> is used, which is a terminal operation, to print the values.
  3. The function multiplies the current value by 2. The initial value is or seed is 2. So it prints 2 at first. Then it prints <em>4</em>, by multiplying 2 by 2, next 4* 2 i.e. 8, etc. It is using the previous value to calculate the current value.

2.2. Stream.generate method to create an infinite stream with Java 8

The <em>Stream</em>.<em>generate</em> method takes only one function and generates the data using that function. Let’s try it with an example program:

import java.util.stream.Stream;

class Example {
    private static long getCurrentTime() {
        return System.currentTimeMillis();
    }

    public static void main(String[] args) {
        Stream < Long > longStream = Stream.generate(Example::getCurrentTime);

        longStream.limit(5).forEach(System.out::println);
    }
}

Output:

1619272695864
1619272695865
1619272695865
1619272695865
1619272695865
  • The getCurrentTime is a method that returns the current time in milliseconds. This function is passed to Stream.generate(). We are using method reference Example::getCurrentTime to pass this function.
  • The Stream.generate calls getCurrentTime repeatedly to produce an infinite stream of long values.
  • Similar to the above example, we are using intermediate operation limit to set a limit of 5 elements and terminal operation foreach to print the values.

2.3 Create an infinite integer stream

In this example, we will create an infinite integer stream. We can use IntStream.iterate or <em>IntStream.generate</em> to create an infinite stream. Let’s take a look at the below example:

import java.util.stream.IntStream;

class Example {
    public static void main(String[] args) {
        IntStream.iterate(0, i - > i + 1).forEach(System.out::println);
    }
}

If you run this program, it will keep running indefinitely. We are using iterate with the seed value 0. On each step, it is adding 1 to the previous value. The forEach terminal operator is used to print the values in a new line.Since we are not using any intermediate operator, it will not stop. Either you need to stop it manually or it will run out of memory error.

2.4. Create an infinite random integer stream

The above example uses IntStream.iterate to create an infinite integer stream. In this example, we will use IntStream.generate. As explained before, this method takes one function and generates the values based on this function. Since we need to create an infinite random integer stream, We can pass one random number generator function to IntStream.generate. Below is the complete program:

import java.util.Random;
import java.util.stream.IntStream;

class Example {
    public static void main(String[] args) {
        IntStream.generate(() - > new Random().nextInt(1000)).forEach(System.out::println);
    }
}

Output:

69
437
932
890
815
156

It might produce different outputs on your machine. Since we are not using any terminal operation here, this program will keep running indefinitely and it will keep printing random numbers until we stop the program.We can use a terminal operation like <em>limit()</em> to limit the number of execution.

2.5 Create an infinite stream with custom objects

In a real-world application, we have to deal with custom objects rather than predefined data types. It works in a similar manner. Let’s take a look at the below program

import java.io.Serializable;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.Stream;

class Student implements Serializable {
    private static final Random random = new Random(1000);
    private final long id;

    public Student() {
        id = random.nextInt(10000);
    }

    @Override
    public String toString() {
        return "Student{" +
            "id=" + id +
            '}';
    }
}

class Example {
    public static void main(String[] args) {
        List < Student > studentList = Stream.generate(Student::new).skip(20).limit(5).collect(Collectors.toList());
        System.out.println(studentList);
    }
}
  • We are creating an <em>infinite stream</em> of Student objects. The generate method creates a new Student object each time.
  • It is adding one id to the Student object once it is created. The id is created by using the random class. Note that we are using a static variable to create the Random object. This will ensure that it uses the same Random object on each object creation.
  • By using skip and limit intermediate operations, we are skipping the first 20 values from the stream and limiting it to 5Collect terminal operation collects the data in a list.

If you run this program, it will print something like below

Student{id=3022}, Student{id=871}, Student{id=2589}, Student{id=2}, Student{id=1693}]

Summary

In this article we saw how to create infinite stream with Java 8.We learned how to create an infinite stream using Stream.iterate() and Stream.generate() methods with different examples. The source code for this article is available on the GitHub repository.