在 Java8 之前,对于大数据量的集合,传统的遍历方式主要是通过 for 循环或者 Iterator 迭代。然而,这种方式在处理大数据量集合时效率并不理想。以电商系统中的订单表为例,通常使用用户 ID 的 Hash 值来实现分表分库,以减少单个表的数据量,提高用户查询订单的速度。但当后台管理员审核订单时,需要将各个数据源的数据查询到应用层之后进行合并操作。比如,查询出过滤条件下的所有订单,并按照某个条件进行排序。在 Java8 之前,通常是通过 for 循环或者 Iterator 迭代来重新排序合并数据,或者通过重新定义 Collections.sort 的 Comparator 方法来实现。但这两种方式对于大数据量系统来说,效率低下。假设一个电商系统中有大量的订单数据需要处理,使用传统的遍历方式,随着数据量的增加,遍历所需的时间会呈线性增长。例如,当有 10 万个订单数据需要遍历筛选并排序时,可能需要花费数秒甚至更长的时间。而且传统方式的代码相对复杂,不够简洁,容易出错。此外,传统方式在处理多数据源的数据合并和排序时,需要手动管理遍历的过程,增加了开发的难度和维护成本。对于开发者来说,不仅要关注遍历的逻辑,还要处理各种边界情况和异常情况,使得代码的可读性和可维护性降低。
Stream 的优势初现
-
简洁强大的示例
通过这个例子可以看出,Stream 结合 Lambda 表达式使得代码更加简洁易懂,同时也提高了开发效率。import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
public class StreamExample {
public static void main(String[] args) {
List<Student> students = new ArrayList<>();
students.add(new Student("小明", 17));
students.add(new Student("小红", 19));
students.add(new Student("小刚", 20));
Map<Boolean, List<Student>> groupedStudents = students.stream()
.collect(Collectors.groupingBy(student -> student.getAge() > 18));
System.out.println("年龄大于 18 岁的学生:");
groupedStudents.get(true).forEach(student -> System.out.println(student.getName()));
}
}
-
类似数据库操作
Stream 如何优化遍历
-
操作分类
-
源码实现
-
操作叠加
-
实例分析
在这个例子中,Stream 操作流程并非表面上的多次遍历集合。首先,通过filter操作筛选出以 “张” 为姓氏的名字,这一步只是记录了操作,并没有真正遍历集合。然后,通过reduce操作进行比较,找到最长的名字。在这个过程中,Stream 利用其内部的高效处理方式,只在需要的时候才进行实际的计算,大大提高了遍历集合的效率。import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class StreamExample {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("张三丰");
names.add("李四");
names.add("张无忌");
names.add("王五");
Optional<String> longestZhangName = names.stream()
.filter(name -> name.startsWith("张"))
.reduce((name1, name2) -> name1.length() > name2.length()? name1 : name2);
if (longestZhangName.isPresent()) {
System.out.println("最长且以张为姓氏的名字:" + longestZhangName.get());
}
}
}
Stream 的并行处理
-
结合 ForkJoin 框架
-
性能测试对比
Tips
-
并行流(Parallel Streams)
parallel()
方法,或者直接在集合上调用parallelStream()
。
示例:
注意,并行并不总是更快,其效率取决于任务的性质和数据的大小。对于小数据集,由于并行处理的开销,顺序处理可能更快。List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 使用并行流计算总和
long sum = numbers.parallelStream().mapToInt(Integer::intValue).sum();
-
避免副作用
不推荐的示例:
List<Integer> numbers = Arrays.asList(1, 2, 3);
List<Integer> squares = new ArrayList<>();
numbers.stream().forEach(n -> {
squares.add(n * n); // 这里修改了外部集合squares
});
推荐的示例:
List<Integer> numbers = Arrays.asList(1, 2, 3);
List<Integer> squares = numbers.stream().map(n -> n * n).collect(Collectors.toList());
-
合理选择终端操作
reduce()
操作可能比collect()
更高效,尤其是在不需要构造复杂结果结构时。-
利用短路操作
anyMatch()
, allMatch()
, 和 findFirst()
。这在处理大数据集时特别有用,因为它们可以在找到第一个匹配项后立即终止操作。
示例:
boolean hasEvenNumber = numbers.stream().anyMatch(n -> n % 2 == 0);
-
避免不必要的收集
示例:
OptionalInt max = numbers.stream().mapToInt(Integer::intValue).max();
-
利用Stream的特化版本
IntStream
, LongStream
, DoubleStream
)可以减少自动装箱/拆箱的开销,提高效率。
标签:遍历,Java,Stream,迭代,处理,集合,操作 From: https://www.cnblogs.com/azwz/p/18451374