详谈 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呢?详情下文源码说明。

相关推荐
雷神乐乐1 分钟前
File.separator与File.separatorChar的区别
java·路径分隔符
小刘|5 分钟前
《Java 实现希尔排序:原理剖析与代码详解》
java·算法·排序算法
南宫理的日知录8 分钟前
99、Python并发编程:多线程的问题、临界资源以及同步机制
开发语言·python·学习·编程学习
逊嘘25 分钟前
【Java语言】抽象类与接口
java·开发语言·jvm
Half-up27 分钟前
C语言心型代码解析
c语言·开发语言
morris13132 分钟前
【SpringBoot】Xss的常见攻击方式与防御手段
java·spring boot·xss·csp
Source.Liu1 小时前
【用Rust写CAD】第二章 第四节 函数
开发语言·rust
monkey_meng1 小时前
【Rust中的迭代器】
开发语言·后端·rust
余衫马1 小时前
Rust-Trait 特征编程
开发语言·后端·rust
monkey_meng1 小时前
【Rust中多线程同步机制】
开发语言·redis·后端·rust