我有以下示例代码:
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()
);
Run Code Online (Sandbox Code Playgroud)
输出如下:
1
Result: 1
-----------
-1
0
1
0
1
2
1
2
3
Result: -1
Run Code Online (Sandbox Code Playgroud)
从这里我看到,在第一种情况下stream真的表现得懒惰 - 我们使用findFirst()所以一旦我们有第一个元素我们的过滤lambda没有被调用.然而,在使用flatMaps的第二种情况下,我们看到尽管找到满足过滤条件的第一个元素(它只是任何第一个元素,因为lambda总是返回true),流的其他内容仍然通过过滤函数被馈送.
我试图理解为什么它表现得像这样,而不是在第一个元素计算后放弃,如第一种情况.任何有用的信息将不胜感激.
关于如何跳过从Files.lines获得的偶数行的问题,我遵循接受的答案filterEven()方法,基于Spliterator<T>接口实现我自己的方法,例如:
public static <T> Stream<T> filterEven(Stream<T> src) {
Spliterator<T> iter = src.spliterator();
AbstractSpliterator<T> res = new AbstractSpliterator<T>(Long.MAX_VALUE, Spliterator.ORDERED)
{
@Override
public boolean tryAdvance(Consumer<? super T> action) {
iter.tryAdvance(item -> {}); // discard
return iter.tryAdvance(action); // use
}
};
return StreamSupport.stream(res, false);
}
Run Code Online (Sandbox Code Playgroud)
我可以通过以下方式使用:
Stream<DomainObject> res = Files.lines(src)
filterEven(res)
.map(line -> toDomainObject(line))
Run Code Online (Sandbox Code Playgroud)
然而,测量这种方法对下一个使用filter()副作用的方法的性能时,我注意到下一个方法表现更好:
final int[] counter = {0};
final Predicate<String> isEvenLine = item -> ++counter[0] % 2 == 0;
Stream<DomainObject> res …Run Code Online (Sandbox Code Playgroud) 在查看的源代码时WrappingSpliterator::trySplit,我对其实现很误解:
@Override
public Spliterator<P_OUT> trySplit() {
if (isParallel && buffer == null && !finished) {
init();
Spliterator<P_IN> split = spliterator.trySplit();
return (split == null) ? null : wrap(split);
}
else
return null;
}
Run Code Online (Sandbox Code Playgroud)
如果您想知道这为什么重要,是因为例如:
Arrays.asList(1,2,3,4,5)
.stream()
.filter(x -> x != 1)
.spliterator();
Run Code Online (Sandbox Code Playgroud)
正在使用它。以我的理解,在流中添加任何中间操作都将导致该代码被触发。
基本上,此方法表示除非流是并行的,否则将此Spliterator视为根本无法拆分的分离器。这对我很重要。在我的一种方法中(这就是我获得该代码的方式),我获得了a Stream作为输入,并使用手动将其“解析”成小块trySplit。你能想到的,例如,我试图做一个findLast从Stream。
这就是我切成小块的愿望,因为我一这样做:
Spliterator<T> sp = stream.spliterator();
Spliterator<T> prefixSplit = sp.trySplit();
Run Code Online (Sandbox Code Playgroud)
我发现,prefixSplit是null的,这意味着我根本无法做任何事情比其他消耗整个sp用forEachRemaning。
这有点不可思议,也许对于何时filter存在有意义。因为在这种情况下(据我所知)唯一Spliterator可以返回的方法是使用某种a buffer …