【问题标题】:'filter()' and 'map()' can be swapped'filter()' 和 'map()' 可以互换
【发布时间】:2021-04-07 04:48:59
【问题描述】:

我有一个简单的流,如下所示:

List<Long> r = l.stream()
                .filter(a->a.getB() % 2 == 0)
                .map(A::getB)
                .collect(Collectors.toList());

但 Intellij 建议我:

'filter()' 和 'map()' 可以互换 检查信息:报告可以简化的流 API 调用链。它允许在遍历集合时避免创建多余的临时对象。例如

  • collection.stream().forEach() → collection.forEach()
  • collection.stream().collect(toList/toSet/toCollection()) → 新的 CollectionType(collection)

Intellij给出的例子很容易理解,但我不明白为什么它建议我map().filter()

我查看了ReferencePipeline 的来源,但没有找到任何线索:map().filter()filter().map() 在涉及与流实现相关的临时对象时没有区别(filter().map() 如果A.b 将有更少的自动装箱@是一个让我更困惑的原始人)。

那么,我是否遗漏了一些流实现,或者这是 Intellij 的误报?

【问题讨论】:

    标签: java intellij-idea java-stream


    【解决方案1】:

    a.getB() 被调用两次 - 一次在过滤器内部,它也是映射函数,因此与其执行两次,不如先使用getB 映射它,然后过滤掉它

    List&lt;Long&gt; r = l.stream().map(A::getB).filter(b-&gt;b % 2 == 0).collect(Collectors.toList());

    编辑

    如果getB 返回long,则mapToLong 可用于避免中间装箱操作。

    List<Long> r = l.stream()
                    .mapToLong(A::getB)
                    .filter(b->b % 2 == 0)
                    .boxed()
                    .collect(Collectors.toList());
    

    样本输出

    使用静态计数器统计get方法的调用次数:

    class A {
        public static int count = 0;
        private long b;
    
        public long getB() {
            count++;
            return b;
        }
    }
    
    List<A> list= List.of(new A(1L), new A(3L), new A(4L));
    
    list.stream() 
        .filter(a -> a.getB()%2 == 0)
        .map(A::getB)
        .collect(Collectors.toList());
    System.out.println(A.count); // returns 4
    

    list.stream()
        .mapToLong(A::getB)
        .filter(b->b % 2 == 0)
        .boxed()
        .collect(Collectors.toList());
    System.out.println(A.count); // returns 3
    

    【讨论】:

    • 但是这里的确切优化是什么?假设我们在列表中有数十亿个元素,并且 %10 个元素是偶数......在映射所有元素更优化之前不会进行过滤。 (我的意思是 CPU 周期)
    • @AliCan 的问题是您需要调用相同的 getter 才能进行过滤,这并不能避免帖子中提到的冗余。
    • @AliCan 过滤函数也在调用getB。假设有 100 个元素,其中 10 个是偶数。由于getB 存在于filtermap 中,因此它将被调用100 + 10 次。但是如果你先映射然后过滤,getB 只会被调用 100 次。
    • @ArvindKumarAvinash 我尝试使用静态计数器运行,对于具有 1 个偶数的 3 个元素的列表,相关代码的计数器值的输出是 4 而不是 6 .
    • 谢谢大家,我的错。确实,现在很明显了。
    猜你喜欢
    • 1970-01-01
    • 2012-12-29
    • 2017-12-08
    • 1970-01-01
    • 2012-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多