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));
}
}
编译并执行此代码后,我收到以下错误。
Exception in thread "main" java.lang.IllegalArgumentException: Stream size exceeds max array size
对流进行排序是否会在无限流中失败?
答案 0 :(得分:4)
“在无限流中对流进行排序会失败吗?”的简单答案是“是。”sorted()
是一个有状态的中间操作,它具有在将任何元素传递给下游操作之前,通过缓冲整个内容并对其进行排序来实现。
理论上,它不需要那样。由于您使用的forEach
已明确指定为以未定义的顺序处理元素,因此在new Random().ints().sorted().forEach(System.out::println);
用例中可以省略排序步骤。但即使你使用forEachOrdered
,也有理论上可以实现的正确答案。由于您的流是无限的并且将重复包含所有int
值,因此正确的排序输出将永远打印-2147483648
(==Integer.MIN_VALUE
),因为这是该流中无限次包含的最小值
但是,为了给出正确的答案,实现将需要特定的代码来处理这种情况,这没有多大实际价值。相反,实现处理这种情况就像流场景的任何其他排序一样,对于无限流将失败。
在这种特定情况下,流具有导致不同的异常异常消息的优化。作为Eugene pointed out,此流的行为类似于Long.MAX_VALUE
(==2⁶³
)元素的固定大小流,而不是真正无限的流。这是公平的,考虑到Random
生成的流将在2⁴⁸值之后重复,因此整个流在结束之前重复32768次而不是永远运行。无论如何,在处理9223372036854775807元素之后,你不太可能目睹这种“突然”的结局。但是这种优化的结果是流将以“流大小超过最大数组大小”消息快速失败,而不是在经过一些处理后失败并出现“OutOfMemoryError”。
如果您删除尺寸信息,例如通过
new Random().ints().filter(x -> true).sorted().forEach(System.out::println);
操作将尝试缓冲,直到java.lang.OutOfMemoryError
失败。
IntStream.generate(new Random()::nextInt).sorted().forEach(System.out::println);
首先不向流提供大小信息。在任何一种情况下,它都不会在排序开始之前对缓冲进行排序。
如果你想在评论中说出“元素限制的分类运行”,你必须在排序前应用限制,例如
new Random().ints().limit(100).sorted().forEach(System.out::println);
尽管仍然使用大小的流更有效,例如
new Random().ints(100).sorted().forEach(System.out::println);
答案 1 :(得分:2)
不,你无法对无限流进行排序。
您的无限流new Random().ints()
产生的整数多于存储在数组(或任何数组)中的整数,后者在后台用于存储要排序的整数。数组当然不能保持无限数量的整数; only a capacity close to Integer.MAX_VALUE
numbers
退后一步,任何人或任何东西如何排序无数个整数?什么都没有,也没有人可以;至少需要无限的时间。
对流进行排序是否会在无限流中失败?
你有点回答了自己的问题; IllegalArgumentException
是失败的具体原因。它只是因为您创建了一个试图执行此操作的程序,并且遇到了Java数组限制。
sorted
方法将在排序任何内容之前尝试读取整个流。它在读取整个流之前不会进行任何中间排序,因此不会进行任何排序,部分或全部。
答案 2 :(得分:0)
这是一个有趣的问题,不幸的是你的关键描述在评论中。首先,它并不是一个真正的无限的"在您的情况下,负责的分裂者RandomIntsSpliterator
甚至会说:
......还通过对待"无限"等同于Long.MAX_VALUE ...
现在有趣的部分,分裂者将报告这些特征:
public int characteristics() {
return (Spliterator.SIZED | Spliterator.SUBSIZED |
Spliterator.NONNULL | Spliterator.IMMUTABLE);
}
好吧,我不知道为什么的原因,但报告SIZED
或SUBSIZED
无限流...可能无关紧要要么(因为你通常用limit
链接这些)。
因为报告了SIZED
(大小为Long.MAX_VALUE
),因此内部SizedIntSortingSink
有一个带有支票的接收器:
if (size >= Nodes.MAX_ARRAY_SIZE)
throw new IllegalArgumentException(Nodes.BAD_SIZE);
显然会失败。
对比IntStream.generate
没有报告SIZED
- 这对我来说很有意义,所以整个输入必须由sorted
缓冲,然后再处理到终端操作;很明显,这会因OutOfMemory
而失败。
证明distinct
不会在这里充当障碍,等待处理所有值,这也很有趣。相反,一旦它知道以前没有看到它,它就可以将它们传递给终端操作:
Random r = new Random();
IntStream.generate(() -> r.nextInt())
.distinct()
.forEach(System.out::println);
在最终死于OutOfMemory
之前,这将持续相当长的一段时间。或者正如Holger在评论中非常好地补充的那样,它也可能会挂起:
IntStream.generate(() -> new Random()
.nextInt(2))
.distinct()
.forEach(System.out::println);