详谈 Java中的list.forEach()和list.stream().forEach() 异同点

涉及的文章链接:ArrayList 循环Remove遇到的坑

一、想总结本篇博客的原因

在日常开发中,需要对集合数据进行或多或少的赋值修改,那么循环赋值或者做一些处理就是最常见的一种操作了,但是用习惯了stream流,所以在循环的过程中就很习惯的使用stream.forEach的方式,可是编码规范却提示用.forEach代替,这是为何呢?

二、stream.forEach 和 .forEach都所处何处?

1).forEach

去定位代码位置,就会发现其在java.lang包下,list.forEach则属于最基本的操作了。因为是基本操作,所以它并不支持并行处理,因此它在处理大量数据时可能不如Stream API高效。

2)stream.forEach

所属java.util.stream下,是Stream API的一部分,用于对流中的每个元素执行给定的操作。使用Stream API的 forEach 允许进行更多的操作,例如过滤、映射等。Stream API提供了更多的灵活性和功能,适用于处理大量数据或进行复杂的操作。

三、stream.forEach 和 .forEach 代码示例

java 复制代码
public static void main(String[] args) {
        List<String> list= Arrays.asList("Sunday","Monday","TuesDay","WednesDay","ThursDay","Friday","Saturday");
        list.forEach(d-> System.out.println("value:"+d));
        list.stream().forEach(f-> System.out.println("value1:"+f));
        list.stream().filter(d->d.length()>3).forEach(s-> System.out.println("value3:"+s));
    }

---总结差异使用

如上demo,当时普通的循环输出的时候,idea会提示我们使用.forEach来替代.stream.forEach,因为仅仅是简单的输出或者赋值,再无其他的操纵;但是当需要对集合做一些额外处理,比如筛选的时候,我们仅仅用.forEach则满足不了需求了,需要先提前将list进行筛选,再进行forEach操作;

四、 parallelStream.forEach

1)parallelStream

表面含义理解也能晓得相比stream而言,他是一种并行的流。它通过 Fork/Join 框架来拆分任务,加速流的处理过程。Fork/Join 的框架是通过把一个大任务不断 fork 成许多子任务,然后多线程执行这些子任务,最后再 Join 这些子任务得到最终结果。

2)parallelStream.forEach demo

① 性能方向

java 复制代码
public static void main(String[] args) {
        List<Integer> numberList=new ArrayList<>();
        for (int i=0;i<1000000;i++){
            numberList.add(i);
        }
        Long startTime=System.currentTimeMillis();
        List<Integer> parallelList=new ArrayList<>();
        numberList.parallelStream().forEach(d->{
            double powValue= Math.pow(d,3);
        });
        Long endTime=System.currentTimeMillis();
        System.out.println("parallel:"+(endTime-startTime));
    }

②准确性方向

java 复制代码
public static void main(String[] args) {
        List<Integer> numberList=new ArrayList<>();
        for (int i=0;i<10000;i++){
            numberList.add(i);
        }
        System.out.println("size="+numberList.size());
        List<Integer> parallelList=new ArrayList<>();
        numberList.parallelStream().forEach(parallelList::add);
        System.out.println("size parallel="+parallelList.size());
    }

看如上代码,执行结果会是什么呢?是size和size parallel都是10000,还是其他?可以自行测试一下,我测试的结果是报错了。

java 复制代码
size=10000
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 4605
	at java.util.ArrayList.add(ArrayList.java:465)
	at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
	at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1384)
	at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)
	at java.util.stream.ForEachOps$ForEachTask.compute(ForEachOps.java:290)
	at java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:731)
	at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
	at java.util.concurrent.ForkJoinTask.doInvoke(ForkJoinTask.java:401)
	at java.util.concurrent.ForkJoinTask.invoke(ForkJoinTask.java:734)
	at java.util.stream.ForEachOps$ForEachOp.evaluateParallel(ForEachOps.java:159)
	at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateParallel(ForEachOps.java:173)
	at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:233)
	at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:485)
	at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:650)
	at com.meta.image.user.service.impl.AchievementServiceImpl.main(AchievementServiceImpl.java:1159)

注:报错分析详情下篇博客。

五、stream.forEach 和 .forEach的CRUD操作

1)java.lang.UnsupportedOperationException 顺带提一嘴

java 复制代码
public static void main(String[] args) {
        List<String> list= Arrays.asList("Sunday","Monday","TuesDay","WednesDay","ThursDay","Friday","Saturday");
        list.forEach(d->{
            System.out.println("value="+d);
            if (d.equals("ThursDay")){
                list.add("other");
            }
        });
    }
java 复制代码
public static void main(String[] args) {
        List<String> list= Arrays.asList("Sunday","Monday","TuesDay","WednesDay","ThursDay","Friday","Saturday");
        list.stream().forEach(d->{
            System.out.println("value="+d);
            if (d.equals("ThursDay")){
                list.add("other");
            }
        });
    }

结果均是一样的

bash 复制代码
value=Sunday
value=Monday
value=TuesDay
value=WednesDay
value=ThursDay
Exception in thread "main" java.lang.UnsupportedOperationException
	at java.util.AbstractList.add(AbstractList.java:148)
	at java.util.AbstractList.add(AbstractList.java:108)
	at com.meta.image.user.service.impl.AchievementServiceImpl.lambda$main$10(AchievementServiceImpl.java:1154)
	at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
	at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:647)
	at com.meta.image.user.service.impl.AchievementServiceImpl.main(AchievementServiceImpl.java:1151)

参考链接:java.lang.UnsupportedOperationException错误的根源

2)add/remove操作

① list.forEach的增删结果

java 复制代码
public static void main(String[] args) {
        List<String> list= new ArrayList<>();
        list.add("Sunday");
        list.add("Monday");
        list.add("Tuesday");
        list.add("Wednesday");
        list.add("Thursday");
        list.add("Friday");
        list.add("Saturday");
        list.forEach(d->{
            System.out.println("value="+d);
            if (d.equals("Thursday")){
                list.add("other");
            }
        });
    }
java 复制代码
public static void main(String[] args) {
        List<String> list= new ArrayList<>();
        list.add("Sunday");
        list.add("Monday");
        list.add("Tuesday");
        list.add("Wednesday");
        list.add("Thursday");
        list.add("Friday");
        list.add("Saturday");
        list.forEach(d->{
            System.out.println("value="+d);
            if (d.equals("Thursday")){
                list.remove(d);
            }
        });
    }

list.forEach的add和remove执行结果均报错:

bash 复制代码
value=Sunday
value=Monday
value=Tuesday
value=Wednesday
value=Thursday
Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList.forEach(ArrayList.java:1262)
	at com.meta.image.user.service.impl.AchievementServiceImpl.main(AchievementServiceImpl.java:1158)

② stream.forEach的增删结果

java 复制代码
public static void main(String[] args) {
        List<String> list= new ArrayList<>();
        list.add("Sunday");
        list.add("Monday");
        list.add("Tuesday");
        list.add("Wednesday");
        list.add("Thursday");
        list.add("Friday");
        list.add("Saturday");
        list.stream().forEach(d->{
            System.out.println("value="+d);
            if (d.equals("Thursday")){
                list.remove(d);
            }
        });
    }

remove报错:

java 复制代码
value=Sunday
value=Monday
value=Tuesday
value=Wednesday
value=Thursday
value=Saturday
value=null
Exception in thread "main" java.lang.NullPointerException
	at com.meta.image.user.service.impl.AchievementServiceImpl.lambda$main$10(AchievementServiceImpl.java:1160)
	at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1384)
	at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:647)
	at com.meta.image.user.service.impl.AchievementServiceImpl.main(AchievementServiceImpl.java:1158)
java 复制代码
public static void main(String[] args) {
        List<String> list= new ArrayList<>();
        list.add("Sunday");
        list.add("Monday");
        list.add("Tuesday");
        list.add("Wednesday");
        list.add("Thursday");
        list.add("Friday");
        list.add("Saturday");
        list.stream().forEach(d->{
            System.out.println("value="+d);
            if (d.equals("Thursday")){
                list.add(d);
            }
        });
    }

add报错:

java 复制代码
value=Sunday
value=Monday
value=Tuesday
value=Wednesday
value=Thursday
value=Friday
value=Saturday
Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1390)
	at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:647)
	at com.meta.image.user.service.impl.AchievementServiceImpl.main(AchievementServiceImpl.java:1158)

③结果及其补救办法

报错结果可以发现 stream.forEach的remove操作是会迭代整个列表的,纵然输出结果在前,但是依旧输出了null,空指针异常了。

参考我的另一篇博客:ArrayList 循环Remove遇到的坑

所以在应用时间过程中,当真的需要对集合进行add或者remove操作的时候,切记要懂得其结果是什么。

六、 总结

程序中若是最简单的赋值操作,那么list.forEach则满足需求;若需要对list做某些处理,比如筛选,则直接用stream.forEach,何时用parallelStream呢?详情下文源码说明。

相关推荐
yyytucj2 分钟前
python--列表list切分(超详细)
linux·开发语言·python
等一场春雨13 分钟前
Java设计模式 八 适配器模式 (Adapter Pattern)
java·设计模式·适配器模式
肖田变强不变秃31 分钟前
C++实现有限元计算 矩阵装配Assembly类
开发语言·c++·矩阵·有限元·ansys
一弓虽35 分钟前
java基础学习——jdbc基础知识详细介绍
java·学习·jdbc·连接池
王磊鑫35 分钟前
Java入门笔记(1)
java·开发语言·笔记
喜欢猪猪43 分钟前
分布式与微服务:构建现代应用的关键架构
开发语言·php
硬件人某某某1 小时前
Java基于SSM框架的社区团购系统小程序设计与实现(附源码,文档,部署)
java·开发语言·社区团购小程序·团购小程序·java社区团购小程序
程序员徐师兄1 小时前
Java 基于 SpringBoot 的校园外卖点餐平台微信小程序(附源码,部署,文档)
java·spring boot·微信小程序·校园外卖点餐·外卖点餐小程序·校园外卖点餐小程序
kucupung1 小时前
【C++基础】多线程并发场景下的同步方法
开发语言·c++
chengpei1471 小时前
chrome游览器JSON Formatter插件无效问题排查,FastJsonHttpMessageConverter导致Content-Type返回不正确
java·前端·chrome·spring boot·json