In stream API we can Use any number of terminal operations to generate desired output

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.

Advertisements

Infinite stream with Java 8

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

static  Stream iterate(T seed,UnaryOperator fn)
1 defined in the Stream class.

  1. static  Stream iterate(T seed,UnaryOperator fn)
    2
  2. static  Stream iterate(T seed,UnaryOperator fn)
    3

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: 

static  Stream iterate(T seed,UnaryOperator fn)
4 and 
static  Stream iterate(T seed,UnaryOperator fn)
5. Intermediate operations return one stream and terminal operations return non-stream values.

Both of these 

static  Stream iterate(T seed,UnaryOperator fn)
4 and 
static  Stream iterate(T seed,UnaryOperator fn)
5 operations are combined to create a stream pipeline.We create a stream pipeline from a source like an 
static  Stream iterate(T seed,UnaryOperator fn)
8 or 
static  Stream iterate(T seed,UnaryOperator fn)
9 and it is followed by zero or more 
static  Stream iterate(T seed,UnaryOperator fn)
4 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. 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);
        }
    }
    1
  2. 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);
        }
    }
    2
  3. 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);
        }
    }
    3
  4. 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);
        }
    }
    4
  5. 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);
        }
    }
    5
  6. 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);
        }
    }
    6

And terminal operations are:

  1. 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);
        }
    }
    7
  2. 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);
        }
    }
    8
  3. 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);
        }
    }
    9
  4. 2
    4
    8
    16
    32
    
    0
  5. 2
    4
    8
    16
    32
    
    1
  6. 2
    4
    8
    16
    32
    
    2
  7. 2
    4
    8
    16
    32
    
    3
  8. 2
    4
    8
    16
    32
    
    4
  9. 2
    4
    8
    16
    32
    
    5
  10. 2
    4
    8
    16
    32
    
    6
  11. 2
    4
    8
    16
    32
    
    7
  12. 2
    4
    8
    16
    32
    
    8
  13. 2
    4
    8
    16
    32
    
    9

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

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);
    }
}
0: generate and iterate. The generate method is defined as below:

static  Stream generate(Supplier s)

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);
    }
}
1 is the type of stream elements and
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);
    }
}
2 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  Stream iterate(T seed,UnaryOperator fn)

Similar to 

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);
    }
}
3, 
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);
    }
}
1 is the type of stream elements. seed is the initial element. This element is used with the function 
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);
    }
}
5 to produce the elements. The function 
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);
    }
}
5 is applied to the previous element to produce the current element.This method returns an infinite sequential ordered stream.The difference between
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);
    }
}
7 and
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);
    }
}
8 is that
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);
    }
}
7 returns one infinite sequential ordered stream but
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);
    }
}
8 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 

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);
    }
}
7 with an example. As we have seen before, the first argument of 
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);
    }
}
7 is a 
1619272695864
1619272695865
1619272695865
1619272695865
1619272695865
3 or starting value and the second argument is a 
1619272695864
1619272695865
1619272695865
1619272695865
1619272695865
4 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:

Advertisements

Advertisements

2
4
8
16
32

Let’s take a look at some important points:

  1. We are using 
    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);
        }
    }
    7 to produce an infinite stream and that is stored in the variable 
    1619272695864
    1619272695865
    1619272695865
    1619272695865
    1619272695865
    
    6.
  2. Intermediate operation 
    1619272695864
    1619272695865
    1619272695865
    1619272695865
    1619272695865
    
    7 is used to limit the output to 
    1619272695864
    1619272695865
    1619272695865
    1619272695865
    1619272695865
    
    8 elements. We can’t use an intermediate operation without a 
    static  Stream iterate(T seed,UnaryOperator fn)
    5 operation. So, 
    import java.util.stream.IntStream;
    
    class Example {
        public static void main(String[] args) {
            IntStream.iterate(0, i - > i + 1).forEach(System.out::println);
        }
    }
    0 is used, which is a terminal operation, to print the values.
  3. The function multiplies the current value by 
    import java.util.stream.IntStream;
    
    class Example {
        public static void main(String[] args) {
            IntStream.iterate(0, i - > i + 1).forEach(System.out::println);
        }
    }
    1. The initial value is or seed is 
    import java.util.stream.IntStream;
    
    class Example {
        public static void main(String[] args) {
            IntStream.iterate(0, i - > i + 1).forEach(System.out::println);
        }
    }
    1. So it prints
    import java.util.stream.IntStream;
    
    class Example {
        public static void main(String[] args) {
            IntStream.iterate(0, i - > i + 1).forEach(System.out::println);
        }
    }
    1 at first. Then it prints 
    import java.util.stream.IntStream;
    
    class Example {
        public static void main(String[] args) {
            IntStream.iterate(0, i - > i + 1).forEach(System.out::println);
        }
    }
    4 by multiplying 
    import java.util.stream.IntStream;
    
    class Example {
        public static void main(String[] args) {
            IntStream.iterate(0, i - > i + 1).forEach(System.out::println);
        }
    }
    1 by 
    import java.util.stream.IntStream;
    
    class Example {
        public static void main(String[] args) {
            IntStream.iterate(0, i - > i + 1).forEach(System.out::println);
        }
    }
    1, next 
    import java.util.stream.IntStream;
    
    class Example {
        public static void main(String[] args) {
            IntStream.iterate(0, i - > i + 1).forEach(System.out::println);
        }
    }
    7 i.e. 
    import java.util.stream.IntStream;
    
    class Example {
        public static void main(String[] args) {
            IntStream.iterate(0, i - > i + 1).forEach(System.out::println);
        }
    }
    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

import java.util.stream.IntStream;

class Example {
    public static void main(String[] args) {
        IntStream.iterate(0, i - > i + 1).forEach(System.out::println);
    }
}
9 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
    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);
        }
    }
    0 is a method that returns the current time in milliseconds. This function is passed to 
    static  Stream iterate(T seed,UnaryOperator fn)
    3. We are using method reference 
    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);
        }
    }
    2 to pass this function.
  • The
    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);
        }
    }
    8 calls 
    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);
        }
    }
    0 repeatedly to produce an infinite stream of 
    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);
        }
    }
    5 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 

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);
    }
}
6to 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 

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);
    }
}
7 to create an infinite integer stream. In this example, we will use 
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);
    }
}
8. As explained before, this method takes one 
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);
    }
}
9 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 
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);
    }
}
8. 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 

69
437
932
890
815
156
1to 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
    69
    437
    932
    890
    815
    156
    
    2 of Student objects. The 
    69
    437
    932
    890
    815
    156
    
    3 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 
    69
    437
    932
    890
    815
    156
    
    4 and 
    1619272695864
    1619272695865
    1619272695865
    1619272695865
    1619272695865
    
    7 intermediate operations, we are skipping the first 
    69
    437
    932
    890
    815
    156
    
    6 values from the stream and limiting it to 5. 
    69
    437
    932
    890
    815
    156
    
    7 terminal operation collects the data in a list.

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

static  Stream iterate(T seed,UnaryOperator fn)
0

Summary

In this article we saw how to create infinite stream with Java 8.We learned how to create an infinite stream using

static  Stream iterate(T seed,UnaryOperator fn)
2 and
static  Stream iterate(T seed,UnaryOperator fn)
3 methods with different examples. The source code for this article is available on the GitHub repository.

How many terminal operations can a stream pipeline have?

A stream can have only one terminal operation, it cannot be chained.

What is the terminal operation available in stream API?

The Stream API gives you two terminal operations to find an element: findFirst() and findAny() . These two methods do not take any argument and return a single element of your stream. To properly handle the case of empty streams, this element is wrapped in an optional object.

How many terminal operations can you perform use on any given stream?

Pipeline of operations can have maximum one terminal operation, that too at the end. Intermediate operations are lazily loaded.

Can we have stream without terminal operation?

Non-Terminal Operations. The non-terminal stream operations of the Java Stream API are operations that transform or filter the elements in the stream. When you add a non-terminal operation to a stream, you get a new stream back as result.