从无限流派生的已排序流无法迭代

Rak*_*yer 1 java java-stream

import java.util.stream.*;
import java.util.*;

class TestInfiniteStream {
    public static void main(String args[]) {
        IntStream infiniteStream = new Random().ints();
        IntStream sortedStream = infiniteStream.sorted();

        sortedStream.forEach(i -> System.out.println(i));
    }
}
Run Code Online (Sandbox Code Playgroud)

编译并执行此代码后,出现以下错误。

Exception in thread "main" java.lang.IllegalArgumentException: Stream size exceeds max array size
Run Code Online (Sandbox Code Playgroud)

在无限流上排序流是否失败?

Hol*_*ger 5

对“ 在无限流上排序流是否失败的简单答案”是“ 是。“” sorted()是有状态的中间操作,在将任何元素传递给下游操作之前,已通过缓冲整个内容并将其排序来实现。

从理论上讲,并不需要那样。由于您正在使用forEach,它已明确指定以未定义的顺序处理元素,因此在您的new Random().ints().sorted().forEach(System.out::println);用例中可以省略排序步骤。但是,即使您使用forEachOrdered,也有一个理论上可以实现的正确答案。由于您的流是无限的并且将重复包含所有int值,因此正确排序的输出将永远打印-2147483648==Integer.MIN_VALUE),因为这是该流中无限次包含的最小值。

但是,要给出正确的答案,实现将需要特定的代码来处理这种情况,这没有太大的实际价值。取而代之的是,实现会像处理流场景的任何其他排序一样处理这种情况,这对于无限流将失败。

在这种特定情况下,流进行了优化,从而产生了不同的异常消息。正如Eugene指出的那样,此流的行为类似于Long.MAX_VALUE==2?³)元素的固定大小的流,而不是真正的无限流。考虑到产生的流Random将在2秒钟后重复出现,这很公平。值,因此整个流在结束之前已经重复了32768次,而不是永远运行。无论如何,在处理9223372036854775807元素之后,您不太可能看到这个“突然的”结束。但是这种优化的结果是,流将通过“流大小超出最大数组大小”消息快速失败,而不是在进行某些处理后因“ OutOfMemoryError”而失败。

如果消除尺寸信息,例如通过

new Random().ints().filter(x -> true).sorted().forEach(System.out::println);
Run Code Online (Sandbox Code Playgroud)

该操作将尝试缓冲直到失败java.lang.OutOfMemoryError。同样的情况发生在

IntStream.generate(new Random()::nextInt).sorted().forEach(System.out::println);
Run Code Online (Sandbox Code Playgroud)

首先,它不向流提供大小信息。无论哪种情况,它都永远不会对任何东西进行排序,因为在排序开始之前就发生了缓冲。

如您在评论中所述,如果要“对元素的某些限制进行排序”,则必须在排序之前应用限制,例如

new Random().ints().limit(100).sorted().forEach(System.out::println);
Run Code Online (Sandbox Code Playgroud)

尽管仍然使用一定大小的流会更有效,例如

new Random().ints(100).sorted().forEach(System.out::println);
Run Code Online (Sandbox Code Playgroud)