Pages

Saturday, August 15, 2020

Java 8 and Infinite Streams - How To Create Infinite Streams

1. Overview

In this article, You'll learn how to create infinite streams and how to use them in java 8.

Use Stream.iterate() and Stream.generate() method to create infinite streams in java. These two are declared as static methods and these are called utility methods.

Before diving into the Infinite Stream concepts, Let us understand the few concepts such as Intermediate and Terminal operation from Stream API.

Java 8 and Infinite Streams - How To Create Infinite Streams

2. Stream Intermediate and Terminal Operations

When the stream is created using stream() method internally pipeline will be created. All the methods invoked on Stream are placed inside the pipeline. Whenever methods called at the beginning of the streams are called as  Intermediate Operations.

All the intermediate operations returns Stream<T> instance such as filter(), map() and flatMap() comes under Intermediate Operations. If any method is not returning Stream then it is not Intermediate Operation.

Stream Intermediate Operations:

filter()

map()

flatMap()

distinct()

sorted()

peek()

limit()

skip()

Note: All intermediate operations are immediately executed and only executed after calling the terminal operations. Even though you add 10 intermediate operations to stream, all of these are not executed unless you call at least one terminal operation.

After using the intermediate operations, to collect the final output you need to use anther list of methods such as collect(), count(), anyMatch().

Final output can be List, Set or Map, Optional values and these are produced by only upon invocation of the following list of terminal methods

toArray()

collect()

count()

reduce()

forEach()

forEachOrdered()

min()

max()

anyMatch()

allMatch()

noneMatch()

findAny()

findFirst()

3. Infinite Streams in Java 8

By the time now you should be good with the intermediate and terminal operations and their execution flows.

Why I was saying in the above sections that stream intermediate methods must be followed by at least one terminal operation because when you call to iterate() or generate() methods start putting the data into the stream then it will be added with infinite values. So if intermediate methods are invoked in the pipeline then it will consume the memory and may end up with the OutOfMemoryError. Because of this reason, terminal operations are needed to end the stream processing.

4. Stream.iterate() Infinite Streams

API description for this method.

"Returns an infinite sequential ordered Stream produced by iterative application of a function f to an initial element seed, producing a Stream consisting of seed, f(seed), f(f(seed)), etc.

The first element (position 0) in the Stream will be the provided seed. For n > 0, the element at position n, will be the result of applying the function f to the element at position n - 1."

Syntax:

static <T> Stream<T> iterate(T seed, UnaryOperator<T> f)
Let us create an infinite number series that starts from 0 and increment by 1.
Stream.iterate(1, i -> i +1);
Now, this is adding the elements to the stream and there is no limit for that. Before calling the terminal operation such as collect(), it is good practice to use limit() method.
package com.javaprogramto.java8.streams.infinite;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class StreamIterateExample {

    public static void main(String[] args) {

        // Creating a infinite Stream
        Stream<Integer> integerInfiniteStream = Stream.iterate(1, i -> i +1);

        List<Integer> first15Numbers = integerInfiniteStream.limit(15).collect(Collectors.toList());

        System.out.println("integerInfiniteStream with limit 15 : "+first15Numbers);

    }
}

Output:
integerInfiniteStream with limit 15 : [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]

First created an unlimited numbers generator using Stream.iterate() method and used the limit(15) method to limit the numbers to 15. Finally, Collect the numbers into List as integers.

5. Stream.generate() Infinite Streams


Stream.generate() is used to generate the infinite series of objects using the given Supplier function.

Syntax:
static <T> Stream<T> generate(Supplier<T> s)
This is mainly suitable in scenarios where generate the constants or random series of objects or numbers.

Returns a Stream of infinite objects.

Example to generate random UUID numbers

package com.javaprogramto.java8.streams.infinite;

import java.util.List;
import java.util.UUID;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class StreamGenerateExample {

    public static void main(String[] args) {

        // Generates the UUID
        Supplier<UUID> randomUUIDSupplier = () -> UUID.randomUUID();

        List<UUID> uuidList = Stream.generate(randomUUIDSupplier).limit(10).collect(Collectors.toList());

        System.out.println("10 random UUID list : "+uuidList);

    }
}

Output:
10 random UUID list : [2800389f-221f-4258-b892-94c74b71da7c, 6adcfba0-2670-40e9-b7c0-320cd11ef965, 894be0cc-c254-4ea9-b7c6-8e3c19e7c34d, 7d4e65d8-e0e4-4759-b98d-17e472f67c3e, 1c53963c-5b73-4fee-bc14-7e9ed9b2b838, 96dc6394-cb99-4547-bfba-88d79487841b, a0b949e1-a424-4a3d-acd9-8b6d5674bd0a, 4748832c-be4f-4b95-a333-392359a06d46, f82b2a23-9aa9-4c67-a9eb-2fdec8cfe62b, 44c33cd9-b3a7-4a23-be5b-def78893bdf1]

6.Stream.iterate() vs Stream.generate()


The main difference between the Stream iterate() and generate() method is iterate() is to get the objects in a sequential order whereas generate() is to get the objects randomly.

import java.util.List;
import java.util.Random;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class StreamGenerateVSIterate {

    public static void main(String[] args) {

        // Example to generate 10 random numbers from 0 to 20.
        Supplier<Integer> infiniteStream1 = () -> new Random().nextInt(20);

        List<Integer> randomNumbers = Stream.generate(infiniteStream1).limit(10).collect(Collectors.toList());

        System.out.println("10 random numbers list : " + randomNumbers);

        // Example to generate 10 random numbers from 0 to 20.
        Stream<Integer> infiniteStream2 = Stream.iterate(0, i -> i + 1);

        List<Integer> first10Numbers = infiniteStream2.limit(10).collect(Collectors.toList());

        System.out.println("first 10 numbers list : " + first10Numbers);

    }
}

Output:
10 random numbers list : [2, 5, 0, 7, 0, 9, 11, 3, 12, 16]
first 10 numbers list : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]


7. Infinite Streams With Custom Objects


As shown in the previous section that generates the random UUID object is an example for custom objects.

Use Stream.generate() to work with an infinite stream of custom objects.

Let us create an example to work with Book custom objects.

Generate infinite Book objects and collect only the first 10 Book objects.

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

public class CustomObjectsStreamGenerateExample {

    public static void main(String[] args) {

        List<Book> uuidList = Stream.generate(Book::create)
                                                            .limit(10)
                                                            .collect(Collectors.toList());

        System.out.println("10 random UUID list : "+uuidList);

    }
}

class Book implements Serializable {

    private int id;
    private String title;
    private double price;

    public Book(int id, String title, double price) {
        this.id = id;
        this.title = title;
        this.price = price;
    }

    public static Book create(){
        int id = new Random().nextInt(10);
        return new Book(id, "name "+id, id * 2.2);
    }

    @Override
    public String toString() {
        return "Book{" +
                "id=" + id +
                ", title='" + title + '\'' +
                ", price=" + price +
                '}';
    }
}
Output:
10 random UUID list : [
Book{id=1, title='name 1', price=2.2}, 
Book{id=0, title='name 0', price=0.0},
 Book{id=6, title='name 6', price=13.200000000000001},
 Book{id=3, title='name 3', price=6.6000000000000005},
 Book{id=1, title='name 1', price=2.2},
 Book{id=0, title='name 0', price=0.0},
 Book{id=1, title='name 1', price=2.2},
 Book{id=1, title='name 1', price=2.2},
 Book{id=2, title='name 2', price=4.4},
 Book{id=5, title='name 5', price=11.0}]

8. Conclusion


In this article, you've seen examples of how to create an infinite stream in java 8.

And also how to limit the infinite loop with the limit() method and collect them into a List.

How to work with custom objects?

Differennces between Stream iterate() and generate() methods.

All the examples are over GitHub.



No comments:

Post a Comment

Please do not add any spam links in the comments section.