相关疑难解决方法(0)

为什么flatMap()之后的filter()在Java流中"不完全"懒惰?

我有以下示例代码:

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),流的其他内容仍然通过过滤函数被馈送.

我试图理解为什么它表现得像这样,而不是在第一个元素计算后放弃,如第一种情况.任何有用的信息将不胜感激.

java lambda java-8 java-stream

70
推荐指数
4
解决办法
6190
查看次数

为什么带副作用的过滤器比基于Spliterator的实现表现更好?

关于如何跳过从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)

java-8 java-stream

7
推荐指数
1
解决办法
448
查看次数

流分离器实现细节

在查看的源代码时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 …

java java-8 java-stream

6
推荐指数
1
解决办法
91
查看次数

标签 统计

java-8 ×3

java-stream ×3

java ×2

lambda ×1