Java 8 引入的 Stream
提供了一种高效、声明式处理集合数据的方式,支持顺序和并行操作,核心特点包括:
filter
, map
)和终端操作(如 collect
, forEach
)实现复杂逻辑。集合创建:Collection.stream()
或 parallelStream()
。
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();
数组创建:Arrays.stream(array)
。
String[] arr = {"a", "b", "c"};
Stream<String> stream = Arrays.stream(arr);
静态方法:Stream.of()
或生成无限流 Stream.iterate()
, Stream.generate()
。
Stream<Integer> numbers = Stream.of(1, 2, 3);
Stream<Integer> infiniteStream = Stream.iterate(0, n -> n + 2); // 0, 2, 4, ...
作用:根据条件筛选元素,保留满足条件的元素。
示例:
List<String> filtered = list.stream()
.filter(s -> s.startsWith("a")) // 保留以"a"开头的字符串
.collect(Collectors.toList());
最佳实践:
筛选出符合条件的元素。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
// 筛选所有偶数
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList()); // [2, 4, 6]
使用逻辑运算符 &&
(与)、||
(或)组合条件。
List<String> words = Arrays.asList("apple", "banana", "cherry", "date");
// 筛选长度大于5 且 以字母a开头的单词
List<String> result = words.stream()
.filter(s -> s.length() > 5 && s.startsWith("b"))
.collect(Collectors.toList()); // ["banana"]
根据对象属性筛选数据。
class User {
String name;
int age;
// 构造方法、getter/setter 省略
}
List<User> users = Arrays.asList(
new User("Alice", 25),
new User("Bob", 17),
new User("Charlie", 30)
);
// 筛选年龄 >= 18 的用户
List<User> adults = users.stream()
.filter(user -> user.getAge() >= 18)
.collect(Collectors.toList()); // [Alice, Charlie]
null
值使用 Objects::nonNull
过滤掉 null
元素。
List<String> listWithNulls = Arrays.asList("a", null, "b", null, "c");
// 过滤掉所有 null 值
List<String> nonNullList = listWithNulls.stream()
.filter(Objects::nonNull)
.collect(Collectors.toList()); // ["a", "b", "c"]
map
是 Java Stream 中最核心的中间操作之一,用于将流中的元素一对一转换为另一种形式。它的本质是通过一个函数(Function
)对每个元素进行映射,生成新的元素流。以下是 map
的深入解析,涵盖使用场景、底层机制、最佳实践与常见问题。
map
的核心特性T
可转换为任意输出类型 R
(如 String
→ Integer
)。作用:将元素转换为另一种类型或提取特定属性。
1. 简单类型转换
// 将字符串转换为大写
List<String> upperCaseList = Arrays.asList("apple", "banana", "cherry").stream()
.map(String::toUpperCase)
.collect(Collectors.toList()); // ["APPLE", "BANANA", "CHERRY"]
// 从User对象中提取name属性
List<String> names = users.stream()
.map(User::getName)
.collect(Collectors.toList());
// 将字符串转换为自定义DTO对象
List<DataDTO> dtos = strings.stream()
.map(s -> {
DataDTO dto = new DataDTO();
dto.setValue(s.length());
dto.setLabel(s.toUpperCase());
return dto;
})
.collect(Collectors.toList());
作用:将嵌套集合(如 List
)展开为单一流。>
示例:
List<List<String>> nestedList = Arrays.asList(
Arrays.asList("a", "b"),
Arrays.asList("c", "d")
);
List<String> flatList = nestedList.stream()
.flatMap(Collection::stream) // 将每个List转换为Stream
.collect(Collectors.toList()); // ["a", "b", "c", "d"]
典型场景:
作用:基于 equals()
和 hashCode()
去重。
示例:
List<Integer> unique = numbers.stream()
.distinct()
.collect(Collectors.toList()); // [1, 2, 3]
关键点:
自定义对象去重:需重写 equals()
和 hashCode()
。
class User {
private Long id;
@Override
public boolean equals(Object o) { /* 基于id比较 */ }
@Override
public int hashCode() { /* 基于id生成 */ }
}
性能注意:对大数据集去重可能消耗内存,可结合 limit
分批次处理。
作用:按自然顺序或自定义比较器排序。
示例:
List<String> sorted = list.stream()
.sorted(Comparator.reverseOrder()) // 逆序排序
.collect(Collectors.toList());
优化建议:
filter
减少待排序数据量。TreeSet
)。作用:skip
跳过前N个元素,limit
限制返回数量。
示例:
List<Integer> result = Stream.iterate(0, n -> n + 1)
.skip(5) // 跳过0-4,从5开始
.limit(10) // 取5-14
.collect(Collectors.toList());
应用场景:
分页查询:模拟数据库分页。
int page = 2, size = 10;
List<User> users = allUsers.stream()
.skip((page - 1) * size)
.limit(size)
.collect(Collectors.toList());
性能注意:对非顺序流(如并行流),skip
和 limit
可能无法保证预期结果。
3. 终端操作(Terminal Operations)
遍历元素:forEach(Consumer
list.stream().forEach(System.out::println);
收集结果:collect(Collector)
List<String> list = stream.collect(Collectors.toList());
Set<String> set = stream.collect(Collectors.toSet());
String joined = stream.collect(Collectors.joining(", "));
统计数量:count()
long count = list.stream().filter(s -> s.length() > 3).count();
匹配检查:
anyMatch(Predicate)
:至少一个元素匹配。allMatch(Predicate)
:所有元素匹配。noneMatch(Predicate)
:没有元素匹配。boolean hasA = list.stream().anyMatch(s -> s.contains("a"));
查找元素:
findFirst()
:返回第一个元素(Optional
)。findAny()
:适用于并行流,返回任意元素。Optional<String> first = list.stream().findFirst();
归约操作:reduce(BinaryOperator
Optional<Integer> sum = Stream.of(1, 2, 3).reduce(Integer::sum); // 6
分组:Collectors.groupingBy()
Map<Integer, List<String>> groupByLength = list.stream()
.collect(Collectors.groupingBy(String::length)); // 按字符串长度分组
分区:Collectors.partitioningBy()
Map<Boolean, List<String>> partition = list.stream()
.collect(Collectors.partitioningBy(s -> s.length() > 3)); // 按条件分为两组
创建并行流:.parallel()
或 parallelStream()
。
List<String> result = list.parallelStream()
.filter(s -> s.length() > 3)
.collect(Collectors.toList());
注意事项:
避免装箱开销:使用 IntStream
, LongStream
, DoubleStream
。
IntStream.range(1, 5).forEach(System.out::println); // 1, 2, 3, 4
LongStream.of(10L, 20L).sum();
// 从用户列表中提取成年用户的姓名
List<String> adultNames = users.stream()
.filter(user -> user.getAge() >= 18)
.map(User::getName)
.collect(Collectors.toList());
// 计算订单总金额
double totalAmount = orders.stream()
.mapToDouble(Order::getAmount)
.sum();
// 将多个订单的商品列表合并并去重
Set<String> allProducts = orders.stream()
.flatMap(order -> order.getProducts().stream())
.collect(Collectors.toSet());
// 按部门分组统计员工平均工资
Map<String, Double> avgSalaryByDept = employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.averagingDouble(Employee::getSalary)
);
filter
, map
比 sorted
, distinct
更高效。IntStream
等)。findFirst()
, limit()
可提前终止流处理。场景 | 传统循环 | Stream 流 |
---|---|---|
简单遍历 | 直接易读 | 代码更简洁,但可能略微性能开销 |
复杂数据处理 | 需多层嵌套循环,代码冗长 | 链式调用,逻辑清晰 |
并行处理 | 需手动管理线程和同步 | 通过 .parallel() 自动并行化 |
函数式编程支持 | 需额外工具类配合 | 原生支持 Lambda 和方法引用 |
通过掌握 Stream 流的常见用法,可以显著提升代码的可读性和开发效率,尤其在处理集合数据时,能够以更简洁的方式实现复杂的数据操作。