1. JDK8 Stream核心概念解析
Stream是JDK8引入的全新API,它允许我们以声明式的方式处理数据集合。与传统的集合操作不同,Stream操作更像是数据库查询——你只需要告诉它你想要什么,而不需要关心具体如何实现。
Stream的核心特点可以概括为:
- 流水线操作:多个操作可以连接起来形成一个流水线
- 内部迭代:迭代操作由Stream API在背后完成
- 延迟执行:只有调用终端操作时才会真正执行
- 并行能力:只需调用parallel()就能实现并行处理
// 典型Stream使用示例 List<String> names = Arrays.asList("John", "Alice", "Bob", "Cathy"); long count = names.stream() .filter(name -> name.length() > 3) .count();2. Stream操作类型详解
2.1 中间操作(Intermediate Operations)
中间操作会返回一个新的Stream,允许我们进行链式调用。常见中间操作包括:
filter(Predicate predicate)
- 过滤不符合条件的元素
- 示例:
.filter(s -> s.startsWith("A"))
map(Function<T,R> mapper)
- 将元素转换为其他形式
- 示例:
.map(String::toUpperCase)
flatMap(Function<T,Stream > mapper)
- 将每个元素转换为流,然后把所有流连接起来
- 示例:
.flatMap(line -> Arrays.stream(line.split(" ")))
distinct()
- 去重(依赖equals方法)
- 示例:
.distinct()
sorted() / sorted(Comparator comparator)
- 排序(自然排序或自定义排序)
- 示例:
.sorted(Comparator.reverseOrder())
peek(Consumer action)
- 查看流经的元素(主要用于调试)
- 示例:
.peek(System.out::println)
2.2 终端操作(Terminal Operations)
终端操作会消耗流,产生一个非流的结果:
forEach(Consumer action)
- 对每个元素执行操作
- 示例:
.forEach(System.out::println)
collect(Collector<T,A,R> collector)
- 将流转换为集合或其他形式
- 示例:
.collect(Collectors.toList())
reduce(...)
- 将流元素组合起来
- 示例:
.reduce(0, Integer::sum)
count()
- 统计元素数量
- 示例:
.count()
anyMatch/allMatch/noneMatch(Predicate predicate)
- 检查是否匹配任何/所有/没有元素
- 示例:
.anyMatch(s -> s.contains("a"))
3. Stream高级特性与应用
3.1 并行流处理
Stream可以轻松实现并行处理:
List<String> names = Arrays.asList("John", "Alice", "Bob", "Cathy"); long count = names.parallelStream() // 只需改为parallelStream .filter(name -> name.length() > 3) .count();注意:并行流并不总是更快,需要考虑数据量、操作复杂度和线程开销等因素。
3.2 原始类型特化流
为避免装箱/拆箱开销,Stream提供了原始类型特化流:
- IntStream
- LongStream
- DoubleStream
IntStream.range(1, 100) // 不包含100 .filter(n -> n % 2 == 0) .sum();3.3 流构建方式
除了从集合创建流,还可以通过多种方式构建流:
- 值创建
Stream<String> stream = Stream.of("A", "B", "C");- 数组创建
String[] array = {"A", "B", "C"}; Stream<String> stream = Arrays.stream(array);- 文件创建
Stream<String> lines = Files.lines(Paths.get("data.txt"));- 函数生成
// 无限流 Stream.iterate(0, n -> n + 2) .limit(10) .forEach(System.out::println); // 随机数流 Stream.generate(Math::random) .limit(5) .forEach(System.out::println);4. 实用Collector操作
Collectors类提供了丰富的收集器实现:
4.1 转换为集合
List<String> list = stream.collect(Collectors.toList()); Set<String> set = stream.collect(Collectors.toSet());4.2 连接字符串
String joined = stream.collect(Collectors.joining(", "));4.3 分组和分区
// 分组 Map<Integer, List<Person>> byAge = persons.stream() .collect(Collectors.groupingBy(Person::getAge)); // 多级分组 Map<Integer, Map<String, List<Person>>> byAgeAndCity = persons.stream() .collect(Collectors.groupingBy(Person::getAge, Collectors.groupingBy(Person::getCity))); // 分区 Map<Boolean, List<Person>> partitioned = persons.stream() .collect(Collectors.partitioningBy(p -> p.getAge() > 18));4.4 统计汇总
IntSummaryStatistics stats = persons.stream() .collect(Collectors.summarizingInt(Person::getAge)); // 包含count, sum, min, average, max5. 性能优化与最佳实践
5.1 流操作顺序优化
流的操作顺序会影响性能:
// 较差的方式 - 先映射再过滤 stream.map(expensiveOperation) .filter(x -> x > 10) .count(); // 更好的方式 - 先过滤再映射 stream.filter(x -> x > 10) .map(expensiveOperation) .count();5.2 避免状态操作
无状态操作(filter, map等)比有状态操作(sorted, distinct等)性能更好,应尽量减少有状态操作的使用。
5.3 短路操作利用
anyMatch、findFirst等短路操作可以在找到结果后立即终止处理,提高效率。
5.4 重用与关闭
流不能被重复使用,尝试重用会抛出IllegalStateException。基于IO的流(如Files.lines)需要手动关闭,或使用try-with-resources:
try (Stream<String> lines = Files.lines(path)) { lines.forEach(System.out::println); }6. 常见问题与解决方案
6.1 流只能被消费一次
Stream<String> stream = Stream.of("A", "B", "C"); stream.forEach(System.out::println); stream.forEach(System.out::println); // 抛出IllegalStateException解决方案:每次需要时重新创建流。
6.2 并行流线程安全问题
List<String> results = new ArrayList<>(); stream.parallel().forEach(s -> results.add(s)); // 线程不安全解决方案:使用线程安全的收集器:
List<String> results = stream.parallel() .collect(Collectors.toList());6.3 无限流处理
Stream.iterate(0, i -> i + 1).forEach(System.out::println); // 无限循环解决方案:总是配合limit()使用:
Stream.iterate(0, i -> i + 1) .limit(100) .forEach(System.out::println);6.4 原始类型与包装类型转换
IntStream intStream = Stream.of(1, 2, 3).mapToInt(x -> x); Stream<Integer> boxed = intStream.boxed();7. 实际应用案例
7.1 文件处理
统计文件中各单词出现频率:
Map<String, Long> wordCount = Files.lines(Paths.get("data.txt")) .flatMap(line -> Arrays.stream(line.split("\\W+"))) .filter(word -> !word.isEmpty()) .collect(Collectors.groupingBy(String::toLowerCase, Collectors.counting()));7.2 数据库查询模拟
模拟分页查询:
List<Person> page = persons.stream() .sorted(Comparator.comparing(Person::getName)) .skip((pageNum - 1) * pageSize) .limit(pageSize) .collect(Collectors.toList());7.3 复杂数据转换
将人员列表转换为树形结构:
Map<Department, Map<Team, List<Employee>>> orgTree = employees.stream() .collect(Collectors.groupingBy(Employee::getDepartment, Collectors.groupingBy(Employee::getTeam)));