System.out.println(
"Result: " +
Stream.of(1, 2, 3)
.filter(i -> {
System.out.println(i);
return true;
})
.findFirst()
.get()
);
System.out.println("-----------");
System.out.println(
"Result: " +
Stream.of(1, 2, 3)
.flatMap(i -> Stream.of(i - 1, i, i + 1))
.flatMap(i -> Stream.of(i - 1, i, i + 1))
.filter(i -> {
System.out.println(i);
return true;
})
.findFirst()
.get()
);
1
Result: 1
-----------
-1
0
1
0
1
2
1
2
3
Result: -1
TL;DR,这个问题在JDK-8075939中得到了解决,并在Java10中得到了修正(并在JDK-8225328中被移植到Java8中)。
在研究实现时(referencePipeline.java
),我们看到方法[link]
@Override
final void forEachWithCancel(Spliterator<P_OUT> spliterator, Sink<P_OUT> sink) {
do { } while (!sink.cancellationRequested() && spliterator.tryAdvance(sink));
}
它将在findfirst
操作中调用。需要特别注意的是sink.cancellationRequested()
,它允许在第一个匹配时结束循环。与[链接]相比较
@Override
public final <R> Stream<R> flatMap(Function<? super P_OUT, ? extends Stream<? extends R>> mapper) {
Objects.requireNonNull(mapper);
// We can do better than this, by polling cancellationRequested when stream is infinite
return new StatelessOp<P_OUT, R>(this, StreamShape.REFERENCE,
StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT | StreamOpFlag.NOT_SIZED) {
@Override
Sink<P_OUT> opWrapSink(int flags, Sink<R> sink) {
return new Sink.ChainedReference<P_OUT, R>(sink) {
@Override
public void begin(long size) {
downstream.begin(-1);
}
@Override
public void accept(P_OUT u) {
try (Stream<? extends R> result = mapper.apply(u)) {
// We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it
if (result != null)
result.sequential().forEach(downstream);
}
}
};
}
};
}
为了说明其中的含义,当stream.iterate(0,i->i+1).findfirst()
按预期工作时,stream.of("“).flatmap(x->stream.iterate(0,i->i+1)).findfirst()
将陷入无限循环。
关于规范,大部分可以在
包规范的“流操作和管道”章节:
…
…懒惰还允许在不必要的时候避免检查所有的数据;对于诸如“查找第一个长度超过1000个字符的字符串”之类的操作,只需要检查足够多的字符串就可以找到一个具有所需特征的字符串,而无需检查源中所有可用的字符串。(当输入流是无限的而不仅仅是大的时,这种行为变得更加重要。)
…
通过一组转换进行筛选和按键分组: 关于如何让它更好的性能,有什么想法吗?谢谢你的帮助!
操作将 转换为包含每个输入元素的零个或多个元素的流,例如。 是否有相反的操作将几个元素分批添加到一个新的元素中? 事实并非如此。reduce(),因为这只产生一个结果 它不是collect(),因为它只填充一个容器(afaiu) 它不是forEach(),因为它只返回void,并且具有副作用 它存在吗?我可以用任何方式模拟它吗?
对终端操作的任何调用都会关闭流,使其无法使用。这个‘特性’带走了很多权力。 我想这不是技术上的原因。这个奇怪的限制背后的设计考虑是什么? 编辑:为了演示我所讲的内容,请考虑以下C#中快速排序的实现:
问题内容: 初始化的优点或区别是什么? 而不是简单地使用: (是:) 非常感谢您的帮助。 问题答案: 惰性存储属性vs存储属性 具有惰性属性而不是存储属性有一些优点。 仅当您读取该属性时,才执行与lazy属性关联的闭包。因此,如果出于某种原因未使用属性(可能是由于用户的某些决定),则可以避免不必要的分配和计算。 您可以使用存储属性的值填充惰性属性。 您可以在内部使用惰性属性关闭
问题内容: 考虑以下代码: 当第一个URL够用时会被要求输入第二个URL吗? 我尝试了一个较小的示例,它看起来像预期的那样工作。即一个一个地处理数据,但是可以依靠这种行为吗?如果没有,在帮助之前打电话吗? 输出: 更新 :如果对实施很重要,请使用官方Oracle JDK8 答案 :根据下面的评论和答案,flatmap部分是惰性的。即完全读取第一个流,并且仅在需要时才读取下一个。渴望读取一个流,但是