'filter()' 和 'map()' 可以互换

Ton*_*ony 6 java intellij-idea java-stream

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

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

但 Intellij 建议我:

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

  • collection.stream().forEach() ?集合.forEach()
  • collection.stream().collect(toList/toSet/toCollection()) ? 新的集合类型<>(集合)

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

我查看了来源ReferencePipeline但没有发现任何线索:map().filter()或者filter().map()在与流实现相关的临时对象方面没有区别(filter().map()如果A.b是一个让我更加困惑的原语,那么自动装箱会更少)。

那么,我是否缺少流实现的某些点,或者这是 Intellij 的误报?

Gau*_*m M 8

a.getB()被调用两次 - 一次在过滤器内部,它也是映射函数,所以不要这样做两次,最好先使用映射getB然后过滤掉它

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

编辑

如果getB返回 a longthenmapToLong可用于避免中间装箱操作。

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

样品输出

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

class A {
    public static int count = 0;
    private long b;

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

然而

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