Java 中 Stream 流的使用详解
什么是 Stream?
Stream 是 Java 8 引入的一种全新的操作集合的方式。它支持通过声明性方式对集合进行复杂的数据操作(如过滤、排序、聚合等),避免使用大量的 for 循环,提高代码的可读性和简洁性。
Stream 具有以下特点:
- 惰性执行:流操作是延迟的,只有在需要时才会执行。
- 链式操作:流的操作可以通过方法链的形式串联。
- 不可变性:Stream 本身不会修改原始数据结构。
Stream 的核心操作
Stream 流的操作分为三类:
- 创建流:通过集合、数组或生成器创建流。
- 中间操作:对数据流进行加工,如
filter
、map
、sorted
等。 - 终端操作:触发流的执行,如
collect
、forEach
、reduce
等。
创建 Stream
1. 从集合创建
List<String> list = List.of("Java", "Python", "C++");
Stream<String> stream = list.stream();
2. 从数组创建
String[] array = {"Apple", "Banana", "Orange"};
Stream<String> stream = Arrays.stream(array);
3. 使用生成器创建
Stream<Integer> infiniteStream = Stream.iterate(0, n -> n + 2);
infiniteStream.limit(5).forEach(System.out::println); // 输出:0, 2, 4, 6, 8
中间操作
1. filter
:过滤数据
List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6);
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
System.out.println(evenNumbers); // 输出:[2, 4, 6]
2. map
:映射转换
List<String> names = List.of("Alice", "Bob", "Charlie");
List<Integer> nameLengths = names.stream()
.map(String::length)
.collect(Collectors.toList());
System.out.println(nameLengths); // 输出:[5, 3, 7]
3. sorted
:排序
List<String> fruits = List.of("Banana", "Apple", "Orange");
List<String> sortedFruits = fruits.stream()
.sorted()
.collect(Collectors.toList());
System.out.println(sortedFruits); // 输出:[Apple, Banana, Orange]
4. distinct
:去重
List<Integer> numbers = List.of(1, 2, 2, 3, 4, 4, 5);
List<Integer> uniqueNumbers = numbers.stream()
.distinct()
.collect(Collectors.toList());
System.out.println(uniqueNumbers); // 输出:[1, 2, 3, 4, 5]
5. limit
和 skip
:截取和跳过
List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6);
List<Integer> limitedNumbers = numbers.stream()
.limit(3)
.collect(Collectors.toList());
System.out.println(limitedNumbers); // 输出:[1, 2, 3]
List<Integer> skippedNumbers = numbers.stream()
.skip(3)
.collect(Collectors.toList());
System.out.println(skippedNumbers); // 输出:[4, 5, 6]
终端操作
1. collect
:收集结果
List<Integer> numbers = List.of(1, 2, 3, 4, 5);
Set<Integer> numberSet = numbers.stream()
.collect(Collectors.toSet());
System.out.println(numberSet); // 输出:[1, 2, 3, 4, 5]
2. forEach
:遍历
List<String> names = List.of("Alice", "Bob", "Charlie");
names.stream().forEach(System.out::println);
3. reduce
:聚合
List<Integer> numbers = List.of(1, 2, 3, 4, 5);
int sum = numbers.stream()
.reduce(0, Integer::sum);
System.out.println(sum); // 输出:15
4. count
:计数
List<String> items = List.of("Apple", "Banana", "Orange");
long count = items.stream()
.filter(item -> item.startsWith("A"))
.count();
System.out.println(count); // 输出:1
5. findFirst
和 findAny
:查找元素
List<Integer> numbers = List.of(1, 2, 3, 4, 5);
Optional<Integer> firstEven = numbers.stream()
.filter(n -> n % 2 == 0)
.findFirst();
firstEven.ifPresent(System.out::println); // 输出:2
案例:复杂数据处理
数据准备
class Employee {
String name;
String department;
double salary;
Employee(String name, String department, double salary) {
this.name = name;
this.department = department;
this.salary = salary;
}
}
List<Employee> employees = List.of(
new Employee("Alice", "HR", 5000),
new Employee("Bob", "IT", 7000),
new Employee("Charlie", "IT", 6000),
new Employee("David", "Finance", 8000)
);
1. 查找 IT 部门的员工姓名
List<String> itEmployees = employees.stream()
.filter(emp -> "IT".equals(emp.department))
.map(emp -> emp.name)
.collect(Collectors.toList());
System.out.println(itEmployees); // 输出:[Bob, Charlie]
2. 按部门分组员工
Map<String, List<Employee>> groupedByDept = employees.stream()
.collect(Collectors.groupingBy(emp -> emp.department));
System.out.println(groupedByDept);
3. 计算所有员工的平均工资
double averageSalary = employees.stream()
.mapToDouble(emp -> emp.salary)
.average()
.orElse(0);
System.out.println(averageSalary); // 输出:6500.0
注意事项
- 避免修改流中的元素:Stream 是不可变的,避免在流中修改元素。
- 流的惰性求值:中间操作只有在终端操作时才会执行。
- 不要重复消费流:流一旦操作完成,就不能再次使用。