电子商务网站平台建设费用,网站开发后期维护更新,网站营销如何做,网站模板下载工具目录 前言1. 基本知识2. 差异之处2.1 执行顺序2.2 串行并行2.3 复杂数据处理2.4 CRUD集合2.5 迭代器 3. 总结4. 彩蛋 前言
典故来源于项目中使用了两种方式的foreach#xff0c;后面尝试体验下有何区别#xff01;
先看代码示例#xff1a;
使用List的forEach#xff1a… 目录 前言1. 基本知识2. 差异之处2.1 执行顺序2.2 串行并行2.3 复杂数据处理2.4 CRUD集合2.5 迭代器 3. 总结4. 彩蛋 前言
典故来源于项目中使用了两种方式的foreach后面尝试体验下有何区别
先看代码示例
使用List的forEach
import java.util.Arrays;
import java.util.List;public class Demo {public static void main(String[] args) {ListString data Arrays.asList(One, Two, Three, Four, Five);// 使用List的forEachdata.forEach(element - {System.out.println(Element: element);});}
}使用Stream API的forEach
import java.util.Arrays;
import java.util.List;public class Demo {public static void main(String[] args) {ListString data Arrays.asList(One, Two, Three, Four, Five);// 使用Stream API的forEachdata.stream().forEach(element - {System.out.println(Element: element);});}
}两者输出结果都为如下 既然两个都输出差不多的结果但两者还是稍微有些小区别具体看下文
1. 基本知识 forEach() 是List接口的一部分用于对列表中的每个元素执行给定的操作。与Stream API的 forEach 不同list的 forEach 不支持并行处理因此它在处理大量数据时可能不如Stream API高效。 适用于简单的迭代和操作不需要复杂的流处理。 stream().forEach() 是Stream API的一部分用于对流中的每个元素执行给定的操作。使用Stream API的 forEach 允许进行更多的操作例如过滤、映射等因为你可以在流上链式调用其他方法。 Stream API提供了更多的灵活性和功能适用于处理大量数据或进行复杂的操作。
两者更多的区别可看下表
代码块概念作用差异之处适用场景list.foreach()List接口中的方法用于对列表中的每个元素执行给定的操作1.通过 forEach()可以对列表中的每个元素进行遍历并在每个元素上执行指定的操作。2.主要用于简单的迭代和操作适用于不需要复杂流处理的场景。1.并行处理 List的 forEach 不支持并行处理因此在处理大量数据时可能不如Stream API高效。Stream API的并行处理机制可以更好地利用多核处理器的性能。2.功能限制 List的 forEach 主要用于简单的迭代操作而没有提供像Stream API那样的丰富中间操作和终端操作限制了其在复杂数据处理场景中的应用。适用于简单的遍历和操作例如对列表中的元素进行打印、计算简单统计量等。不适用于需要复杂处理逻辑的情况。stream().foreach()Java 8引入的Stream API中的方法用于对流中的每个元素执行给定的操作。1.通过 forEach()可以对流中的每个元素进行遍历并在每个元素上执行指定的操作。2.Stream API提供了一种更为函数式的方式来处理数据允许链式调用其他方法如过滤、映射、排序等。1.链式调用 使用Stream API的 forEach 允许在流上进行链式调用其他方法使得可以进行更多的操作。这种链式调用是函数式编程的一种特征使代码更为灵活和表达性强。2.功能丰富 Stream API提供了更多的中间操作和终端操作例如 map、filter、reduce 等这些操作可以组合使用实现更复杂的数据处理逻辑。适用于处理大量数据或进行复杂的操作例如对数据进行变换、过滤、聚合等。Stream API的并行处理机制也使得在多核处理器上能够更高效地处理大规模数据。
总结
2. 差异之处
此处的差异之处主要讲解stream().foreach() 比 list.foreach()多的点
2.1 执行顺序
stream流中如果结合parallelStream()会有不一样的特性
import java.util.Arrays;
import java.util.List;public class Demo {public static void main(String[] args) {ListString data Arrays.asList(One, Two, Three, Four, Five);// 使用Stream API中的parallelStream forEachdata.parallelStream().forEach(element - {System.out.println(Element: element);});}
}截图如下 在使用 parallelStream() 进行并行处理时元素的处理顺序可能不会按照原始列表中的顺序输出。 并行流会将数据分成多个块然后并行处理这些块最后将结果合并。
因此并行处理的结果可能在不同的块之间交错导致输出的顺序不再按照原始列表的顺序。
想要保持顺序可以使用 forEachOrdered() 方法它会保证按照流的遍历顺序输出结果并行流的情况下保持元素的遍历顺序但可能会牺牲一些并行处理的性能优势。示例如下
import java.util.Arrays;
import java.util.List;public class Demo {public static void main(String[] args) {ListString data Arrays.asList(One, Two, Three, Four, Five);// 使用Stream API的parallelStream().forEachOrdereddata.parallelStream().forEachOrdered(element - {System.out.println(Element: element);});}
}截图如下 2.2 串行并行
对应上章节继续科普parallelStream()一般来说并行的输出速度比较快用于对顺序输出不讲究但是大量的数据处理可以用这个
对于少量的数据来说效果不明显甚至会高于list.foreach()所以根据情况而来一般数据处理多可以应用parallelStream()
import java.util.ArrayList;
import java.util.List;public class Demo {public static void main(String[] args) {ListInteger numbers new ArrayList();for (int i 1; i 1000000; i) {numbers.add(i);}// 使用 Stream.forEach 进行并行处理long startTime System.currentTimeMillis();numbers.parallelStream().forEach(element - {// 模拟一些耗时的操作Math.pow(element, 2);});long endTime System.currentTimeMillis();System.out.println(Time taken with Stream.forEach (parallel): (endTime - startTime) ms);}
}截图如下所示 原本以为使用list.foreach()时间会更长反而时间更短
import java.util.ArrayList;
import java.util.List;public class Demo {public static void main(String[] args) {ListInteger numbers new ArrayList();for (int i 1; i 1000000; i) {numbers.add(i);}// 使用 List.forEach 进行串行处理long startTime System.currentTimeMillis();numbers.forEach(element - {// 模拟一些耗时的操作Math.pow(element, 2);});long endTime System.currentTimeMillis();System.out.println(Time taken with List.forEach (sequential): (endTime - startTime) ms);}
}截图如下 可能是由于数据规模较小、并行处理带来的额外开销以及模拟的耗时操作较短导致并行流的性能没有得到有效提升。
并行处理的优势在于处理大规模数据时更为显著。
2.3 复杂数据处理
Stream API提供了丰富的中间操作和终端操作使得能够进行更复杂的数据处理。
下面举例说明 filter()、map()、reduce() 这几个常用的操作方法。
filter() 用于对流中的元素进行过滤只保留满足某个条件的元素
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;public class Demo {public static void main(String[] args) {ListString data Arrays.asList(One, Two, Three, Four, Five);// 过滤出长度大于3的元素ListString filteredList data.stream().filter(element - element.length() 3).collect(Collectors.toList());System.out.println(Filtered List: filteredList);}
}截图如下 map() 用于对流中的每个元素进行映射转换生成一个新的流
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;public class Demo {public static void main(String[] args) {ListString data Arrays.asList(One, Two, Three, Four, Five);// 将每个元素转换为大写ListString uppercasedList data.stream().map(String::toUpperCase).collect(Collectors.toList());System.out.println(Uppercased List: uppercasedList);}
}截图如下 reduce() 用于将流中的元素逐个进行操作最终得到一个结果
import java.util.Arrays;
import java.util.List;
import java.util.Optional;public class Demo {public static void main(String[] args) {ListInteger numbers Arrays.asList(1, 2, 3, 4, 5);// 对所有元素求和OptionalInteger sum numbers.stream().reduce((x, y) - x y);sum.ifPresent(result - System.out.println(Sum: result));}
}使用 OptionalInteger 主要是为了处理可能没有元素的情况避免返回 null。
考虑到 numbers 可能为空如果直接使用 reduce((x, y) - x y)当 numbers 为空时会得到 null而这可能导致空指针异常。
截图如下 拓展1 对于上述代码中都有使用到Collectors.toList() Collectors.toList() 是Collectors 工具类提供的一个静态方法它用于将流中的元素收集到一个 List 集合中。 在Stream API中对流进行一系列的操作后通常会希望将结果收集到一个集合中以便后续的操作或输出。 在实际应用中还可以使用其他的 Collectors 方法如 toSet()、toMap() 等以便根据需求将元素收集到不同的集合类型中。 拓展2 对于上述reduce中使用到了Optional 类 Optional 是Java 8引入的一个类用于处理可能为null的值。 它的设计目的是避免使用null减少空指针异常的发生并提供更安全、清晰的代码。 在上述代码中通过使用 OptionalInteger可以更安全地处理可能为空的情况。如果 numbers 为空reduce 操作的结果将是 Optional.empty()而不是 null。
import java.util.Arrays;
import java.util.List;
import java.util.Optional;public class ReduceWithOptionalExample {public static void main(String[] args) {ListInteger numbers Arrays.asList(1, 2, 3, 4, 5);// 使用Optional处理可能为空的情况OptionalInteger sum numbers.stream().reduce((x, y) - x y);// 判断Optional是否包含值if (sum.isPresent()) {System.out.println(Sum: sum.get());} else {System.out.println(The list is empty.);}}
}2.4 CRUD集合
当使用 list.foreach() 或 stream().foreach() 进行遍历操作时如果在遍历过程中对集合进行了增、删、改的操作可能会导致 ConcurrentModificationException 异常。
一、list.foreach()的增删
增加数据的Demo代码
import java.util.*;public class Demo {public static void main(String[] args) {ListString data new ArrayList();data.add(One);data.add(Two);data.add(Three);// 使用 List.forEach 遍历并在遍历过程中增加元素data.forEach(element - {System.out.println(Element: element);if (element.equals(Two)) {data.add(Four); // 在遍历过程中增加元素}});}
}以及删除数据Demo
import java.util.*;public class Demo {public static void main(String[] args) {ListString data new ArrayList();data.add(One);data.add(Two);data.add(Three);// 使用 List.forEach 遍历并在遍历过程中删除元素data.forEach(element - {System.out.println(Element: element);if (element.equals(Two)) {data.remove(element); // 在遍历过程中删除元素可能导致异常}});}
}最终的结果截图如下 二、list.stream().foreach()的增删
增加数据的Demo代码
import java.util.*;public class Demo {public static void main(String[] args) {ListString data new ArrayList();data.add(One);data.add(Two);data.add(Three);// 使用 Stream.forEach 遍历并在遍历过程中增加元素data.stream().forEach(element - {System.out.println(Element: element);if (element.equals(Two)) {data.add(Four); // 在遍历过程中增加元素}});}
}截图如下 以及删除数据Demo
import java.util.*;public class Demo {public static void main(String[] args) {ListString data new ArrayList();data.add(One);data.add(Two);data.add(Three);// 使用 Stream.forEach 遍历并在遍历过程中删除元素data.stream().forEach(element - {System.out.println(Element: element);if (element.equals(Two)) {data.remove(element); // 在遍历过程中删除元素可能导致异常}});}
}截图如下: 通过上述四个程序的执行结果可以得到什么体会呢 stream().foreach()在操作集合的CRUD的时候执行错误之后还是会迭代整个列表才看到异常 这也证明stream().foreach()为并行执行处理数据而不是串行 那有办法解决这种异常的情况么又能对集合进行CRUD答案是有的可看如下正文 2.5 迭代器
通过上述的阅读急需需要一个对集合的CRUD做一个安全性的迭代 于是有了如下的解决方式
import java.util.*;public class Demo {public static void main(String[] args) {ListString data new ArrayList();data.add(One);data.add(Two);data.add(Three);// 使用迭代器遍历并在条件满足时删除元素IteratorString iterator data.iterator();while (iterator.hasNext()) {String element iterator.next();System.out.println(Element: element);if (element.equals(Two)) {iterator.remove(); // 尝试在不支持结构性修改的列表上进行删除操作抛出异常}}System.out.println(Modified List: data);}
}截图如下 3. 总结
总结起来对于上面的代码学习主要涉及了两种遍历集合的方式List.forEach() 和 List.stream().forEach()。下面对这两种方式的区别进行总结
List.forEach() 方法 遍历是在当前线程中按顺序执行的对集合元素的操作是同步的。 适用于简单的、顺序执行的遍历操作。 不支持并行操作不保证源数据的顺序。
List.stream().forEach() 方法 可以利用并行流进行多线程处理提高遍历效率不保证源数据的顺序。 适用于更复杂的、并行处理的遍历操作可以配合 Stream 的其他操作进行更灵活的数据处理。
4. 彩蛋
对于上述中的代码有一个需要注意的点 不管是list.foreach()还是list.stream().foreach()集合的CRUD都会出现会爆Exception in thread main java.lang.UnsupportedOperationException的错误举一个例子。
import java.util.*;public class Demo {public static void main(String[] args) {ListString data Arrays.asList(One, Two, Three);// 使用 List.forEach 遍历并在遍历过程中删除元素data.forEach(element - {System.out.println(Element: element);if (element.equals(Two)) {data.remove(element); // 在遍历过程中删除元素可能导致异常}});}
}截图如下 主要的原因在于
Arrays.asList(One, Two, Three, Four, Five) 创建的列表是由数组支持的固定大小的列表。
这意味着该列表不支持结构性修改操作如添加、删除并且会在尝试进行这些操作时抛出UnsupportedOperationException 异常。