Stream
是一个接口,因此每当有人抓住Stream
对象时,就会隐藏许多特定于实现的细节。
例如,请使用以下代码:
List<String> list = new ArrayList<>();
...
int size = list.stream()
.count();
它是以恒定还是线性时间运行?或者这个:
Set<String> set = new TreeSet<>();
...
set.stream()
.sorted()
.forEach(System.out::println);
那是O(n)还是O(n log n)?
一般来说,标准集合返回的流实现有多专业化?
答案 0 :(得分:15)
它是在恒定时间还是线性时间运行?
当前实现以线性时间运行:
public final long count() {
return mapToLong(e -> 1L).sum();
}
但是,在某些情况下可以在某些情况下以恒定时间运行,这可以得到改善(这是某个地方的RFE)。
如何?流由流源描述,零个或多个中间操作,终端操作(此处,count()
是终端操作)。流实现维护有关源的一组特征,并知道操作如何修改特征。例如,由Collection支持的流具有特征SIZED
,而由Iterator支持的流的大小不是。类似地,操作map()
是大小保留的,但操作filter()
破坏了任何大小的先验知识。流实现在启动终端操作之前知道管道的组合特征,因此它知道源是否大小以及是否所有阶段都是大小保留的,并且在这种情况下,可以简单地询问源的大小并绕过所有实际的流计算。 (但是Java 8中的实现不会发生这种情况。)
请注意,流不需要专门用于支持这一点; Collection类使用知道其特性的Spliterator
创建流,因此不需要专门的集合实现,只需更新共享实现以利用这些特定信息。
答案 1 :(得分:2)
sorted()
方法不会改变大小,因此从理论上讲,未来的实现可能会在stream().sorted().count()
时间内执行O(1)
链。
在https://github.com/speedment/speedment查看Stream接口的[speedment]开源实现。这些流可以内省自己的管道并优化流中的一个或几个步骤。