Java 8 and Infinite Streams – Java 8和无限流(Infinite Streams

最后修改: 2017年 2月 12日

1. Overview


In this article, we will be looking at a java.util.Stream API and we’ll see how we can use that construct to operate on an infinite stream of data/elements.

在这篇文章中,我们将看到一个java.util.Stream API,我们将看到我们如何使用该结构来操作无限的数据/元素的流。

The possibility of working on the infinite sequence of elements is predicated on the fact that streams are built to be lazy.


This laziness is achieved by a separation between two types of the operations that could be executed on streams: intermediate and terminal operations.


2. Intermediate and Terminal Operations


All Stream operations are divided into intermediate and terminal operations and are combined to form stream pipelines.


A stream pipeline consists of a source (such as a Collection, an array, a generator function, an I/O channel, or infinite sequence generator); followed by zero or more intermediate operations and a terminal operation.


2.1. Intermediate Operations


Intermediate operations are not executed until some terminal operation is invoked.


They’re composed forming a pipeline of a Stream execution. The intermediate operation can be added to a Stream pipeline by methods:


  • filter()
  • map()
  • flatMap()
  • distinct()
  • sorted()
  • peek()
  • limit()
  • skip()

All Intermediate operations are lazy, so they’re not executed until a result of a processing is actually needed.


Basically, intermediate operations return a new stream. Executing an intermediate operation does not actually perform any operation, but instead creates a new stream that, when traversed, contains the elements of the initial stream that match the given predicate.


As such, traversal of the Stream doesn’t begin until the terminal operation of the pipeline is executed.


That is very important property, specifically important for infinite streams – because it allows us to create streams that will be actually invoked only when a Terminal operation is called.


2.2. Terminal Operations


Terminal operations may traverse the stream to produce a result or a side effect.


After the terminal operation is performed, the stream pipeline is considered consumed, and can no longer be used. In almost all cases, terminal operations are eager, completing their traversal of the data source and processing of the pipeline before returning.


The eagerness of a terminal operation is important concerning infinite streams because at the moment of processing we need to think carefully if our Stream is properly bounded by, for example, a limit() transformation. Terminal operations are:


  • forEach()
  • forEachOrdered()
  • toArray()
  • reduce()
  • collect()
  • min()
  • max()
  • count()
  • anyMatch()
  • allMatch()
  • noneMatch()
  • findFirst()
  • findAny()

Each of these operations will trigger execution of all intermediate operations.


3. Infinite Streams


Now that we understand these two concepts – Intermediate and Terminal operations – we’re able to write an infinite stream that leverage laziness of Streams.


Let’s say that we want to create an infinite stream of elements from zero that will be incremented by two. Then we need to limit that sequence before calling terminal operation.


It is crucial to use a limit() method before executing a collect() method that is a terminal operation, otherwise our program will run indefinitely:


// given
Stream<Integer> infiniteStream = Stream.iterate(0, i -> i + 2);

// when
List<Integer> collect = infiniteStream

// then
assertEquals(collect, Arrays.asList(0, 2, 4, 6, 8, 10, 12, 14, 16, 18));

We created an infinite stream using an iterate() method. Then we called a limit() transformation and a collect() terminal operation. Then in our resulting List, we will have first 10 elements of an infinite sequence due to a laziness of a Stream.

我们使用iterate() 方法创建了一个无限的流。然后我们调用了一个limit() 转换和一个collect() 终端操作。然后在我们生成的List中,由于Stream的懒惰,我们将拥有一个无限序列的前10个元素

4. Infinite Stream of a Custom Type of Elements


Let’s say that we want to create an infinite stream of random UUIDs.


The first step to achieving this using Stream API is to create a Supplier of those random values:

使用Stream API实现这一目标的第一步是创建这些随机值的Supplier

Supplier<UUID> randomUUIDSupplier = UUID::randomUUID;

When we define a supplier we can create an infinite stream using a generate() method:


Stream<UUID> infiniteStreamOfRandomUUID = Stream.generate(randomUUIDSupplier);

Then we could take a couple of elements from that stream. We need to remember to use a limit() method if we want our program to finish in a finite time:

然后我们可以从该流中提取几个元素。如果我们希望我们的程序在有限的时间内完成,我们需要记住使用limit() 方法。

List<UUID> randomInts = infiniteStreamOfRandomUUID

We use a skip() transformation to discard first 10 results and take the next 10 elements. We can create an infinite stream of any custom type elements by passing a function of a Supplier interface to a generate() method on a Stream.

我们使用一个skip() 转换来丢弃前10个结果,并取下后10个元素。我们可以通过向Stream上的generate()方法传递Supplier接口的函数来创建一个任何自定义类型元素的无限流。

6. Do-While – the Stream Way

6.Do-While – the Stream Way

Let’s say that we have a simple do..while loop in our code:


int i = 0;
while (i < 10) {

We are printing i counter ten times. We can expect that such construct can be easily written using Stream API and ideally, we would have a doWhile() method on a stream.

我们正在打印i counter十次。我们可以期待使用Stream API很容易写出这样的结构,理想情况下,我们会在一个流上有一个doWhile() 方法。

Unfortunately, there is no such method on a stream and when we want to achieve functionality similar to standard do-while loop we need to use a limit() method:


Stream<Integer> integers = Stream
  .iterate(0, i -> i + 1);

We achieved same functionality like an imperative while loop with less code, but call to the limit() function is not as descriptive as it would be if we had a doWhile() method on a Stream object.


5. Conclusion


This article explains how we can use the Stream API to create infinite streams. These, when used together with transformations such as limit() – can make some scenarios quite a bit easier to understand and implement.

这篇文章解释了我们如何使用Stream API 来创建无限的流。当与诸如limit() – 这样的转换一起使用时,可以使一些场景更容易理解和实现。

The code supporting of all these examples can be found in the GitHub project – this is a Maven project, so it should be easy to import and run as it is.