Java ';过滤器()';和';map()';可以交换

Java ';过滤器()';和';map()';可以交换,java,intellij-idea,java-stream,Java,Intellij Idea,Java Stream,我有一个简单的流,如下所示: List r=l.stream() .filter(a->a.getB()%2==0) .map(A::getB) .collect(Collectors.toList()); 但Intellij建议我: “filter()”和“map()”可以互换 检验信息:报告流API调用链,可以简化。它允许在遍历集合时避免创建冗余的临时对象。e、 g collection.stream().forEach()→ collection.forEach() collectio

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

List r=l.stream()
.filter(a->a.getB()%2==0)
.map(A::getB)
.collect(Collectors.toList());
但Intellij建议我:

“filter()”和“map()”可以互换 检验信息:报告流API调用链,可以简化。它允许在遍历集合时避免创建冗余的临时对象。e、 g

  • collection.stream().forEach()→ collection.forEach()
  • collection.stream().collect(toList/toSet/toCollection())→ 新集合类型(集合)
Intellij给出的示例很容易理解,但我不明白为什么它建议我使用
map().filter()

我查看了
ReferencePipeline
的源代码,但没有找到任何线索:
map().filter()
filter().map()
对于与流实现相关的临时对象没有什么区别(
filter().map()
如果
A.b
是一个原语,那么它的自动装箱功能就会减少,这让我更加困惑)

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

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

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

编辑

如果
getB
返回
long
,则可以使用
mapToLong
来避免中间装箱操作

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

使用静态计数器对get方法的调用进行计数:

A类{
公共静态整数计数=0;
私人长b;
公共长getB(){
计数++;
返回b;
}
}
List List=List.of(新A(1L)、新A(3L)、新A(4L));
list.stream()
.filter(a->a.getB()%2==0)
.map(A::getB)
.collect(Collectors.toList());
System.out.println(A.count);//返回4
鉴于

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

但这里的具体优化是什么?假设列表中有数十亿个元素,其中%10个元素是偶数。。。在映射所有更优化的元素之前不会进行过滤。(我的意思是CPU周期)@AliCan关键是你需要调用同一个getter来进行筛选,这并不能避免文章中提到的冗余。@AliCan筛选函数也在调用
getB
。假设有100个元素,10个元素是偶数。由于
getB
filter
map
中都存在,因此它将被调用100+10次。但是如果你映射然后过滤,
getB
只会被调用100次。@ArvindKumarAvinash我试着用一个静态计数器运行,对于一个包含3个元素的列表,1个偶数,所讨论的代码的计数器值的输出是
4
而不是
6
。谢谢大家,我的错了。事实上,现在很明显。