Java Stream API 详解:从创建到高级应用
1. Java Stream 流概述Java 8 引入的 Stream API 彻底改变了我们处理集合数据的方式。记得第一次接触 Stream 时我被它简洁的链式调用惊艳到了——原本需要十几行代码的集合操作现在只需一行就能搞定。Stream 不是数据结构它更像是一个高级迭代器允许我们以声明式的方式处理数据。与传统的集合操作相比Stream 有两个核心优势一是支持并行处理只需调用 parallel() 方法二是采用惰性求值机制。我曾在处理百万级数据时做过对比测试合理使用并行流能使处理速度提升 3-5 倍。2. 创建 Stream 的 7 种方式2.1 从集合创建这是最常见的创建方式ListString list Arrays.asList(a, b, c); StreamString stream list.stream(); // 顺序流 StreamString parallelStream list.parallelStream(); // 并行流2.2 使用 Arrays.stream()处理数组时特别方便int[] numbers {1, 2, 3, 4, 5}; IntStream intStream Arrays.stream(numbers);2.3 使用 Stream.of()适合已知元素的场景StreamString stream Stream.of(Java, Python, Go);2.4 使用 Stream.iterate()生成无限流记得用limit限制Stream.iterate(0, n - n 2) .limit(10) .forEach(System.out::println);2.5 使用 Stream.generate()同样生成无限流Stream.generate(Math::random) .limit(5) .forEach(System.out::println);2.6 从文件创建处理大文件时特别高效try (StreamString lines Files.lines(Paths.get(data.txt))) { lines.forEach(System.out::println); }2.7 其他特殊流比如处理字符串IntStream charsStream Hello.chars();3. 中间操作详解3.1 filter() 过滤// 过滤出长度大于3的字符串 list.stream() .filter(s - s.length() 3) .forEach(System.out::println);注意filter() 的谓词应该保持无状态否则在并行流中会出现不可预测的结果。3.2 map() 映射类型转换的利器// 字符串转大写 ListString upperCaseList list.stream() .map(String::toUpperCase) .collect(Collectors.toList());3.3 flatMap() 扁平化处理嵌套集合时特别有用ListListString nestedList ...; ListString flatList nestedList.stream() .flatMap(Collection::stream) .collect(Collectors.toList());3.4 distinct() 去重ListInteger numbers Arrays.asList(1, 2, 1, 3, 3, 2, 4); numbers.stream() .distinct() .forEach(System.out::println);3.5 sorted() 排序// 自然排序 list.stream().sorted(); // 自定义排序 list.stream() .sorted((a, b) - b.length() - a.length()) .forEach(System.out::println);3.6 peek() 调试调试Stream流水线的好帮手ListString result list.stream() .filter(s - s.length() 3) .peek(s - System.out.println(Filtered: s)) .map(String::toUpperCase) .peek(s - System.out.println(Mapped: s)) .collect(Collectors.toList());4. 终端操作全解析4.1 forEach() 遍历list.stream().forEach(System.out::println);注意在并行流中forEach不保证顺序如果需要保持顺序使用forEachOrdered。4.2 collect() 收集最强大的终端操作// 转List ListString list stream.collect(Collectors.toList()); // 转Set SetString set stream.collect(Collectors.toSet()); // 转Map MapString, Integer map stream.collect( Collectors.toMap(Function.identity(), String::length));4.3 reduce() 归约// 求和 int sum numbers.stream().reduce(0, Integer::sum); // 连接字符串 OptionalString concat strings.stream() .reduce(String::concat);4.4 min()/max() 极值OptionalString longest list.stream() .max(Comparator.comparingInt(String::length));4.5 count() 计数long count list.stream().filter(s - s.length() 3).count();4.6 anyMatch()/allMatch()/noneMatch()boolean hasLongWord list.stream().anyMatch(s - s.length() 10);4.7 findFirst()/findAny()OptionalString first list.stream().findFirst();5. 高级应用技巧5.1 并行流优化ListString result largeList.parallelStream() .filter(s - s.length() 5) .collect(Collectors.toList());注意并行流不是万能的当数据量小或操作简单时反而可能更慢。5.2 自定义收集器实现Collector接口CollectorString, ?, ListString toList Collector.of( ArrayList::new, List::add, (left, right) - { left.addAll(right); return left; }, Characteristics.IDENTITY_FINISH );5.3 流的分组和分区// 分组 MapInteger, ListString groupByLength list.stream().collect(Collectors.groupingBy(String::length)); // 分区 MapBoolean, ListString partition list.stream().collect(Collectors.partitioningBy(s - s.length() 5));5.4 流的拼接StreamString combined Stream.concat(stream1, stream2);5.5 原始类型流避免装箱开销IntStream.range(1, 100) .filter(n - n % 2 0) .average() .ifPresent(System.out::println);6. 性能优化与陷阱6.1 短路操作优化boolean hasLongWord list.stream() .filter(s - s.length() 10) .findFirst() .isPresent();6.2 避免重复计算错误示例// 错误每次都会新建Pattern list.stream() .filter(s - s.matches(\\d)) .count();正确做法Pattern pattern Pattern.compile(\\d); list.stream() .filter(s - pattern.matcher(s).matches()) .count();6.3 注意流的状态避免在lambda中使用可变状态// 错误示例 int[] counter new int[1]; list.stream().forEach(s - counter[0]);6.4 并行流的注意事项// 错误使用了有状态的lambda ListString sorted list.parallelStream() .sorted(Comparator.comparingInt(s - s.length())) .collect(Collectors.toList());7. 实战案例7.1 统计单词频率MapString, Long wordCount Files.lines(Paths.get(book.txt)) .flatMap(line - Arrays.stream(line.split(\\W))) .filter(word - !word.isEmpty()) .collect(Collectors.groupingBy(String::toLowerCase, Collectors.counting()));7.2 处理CSV文件ListPerson persons Files.lines(Paths.get(persons.csv)) .skip(1) // 跳过标题行 .map(line - { String[] parts line.split(,); return new Person(parts[0], Integer.parseInt(parts[1])); }) .collect(Collectors.toList());7.3 多层集合处理ListOrder orders ...; ListProduct products orders.stream() .flatMap(order - order.getItems().stream()) .map(OrderItem::getProduct) .distinct() .collect(Collectors.toList());7.4 分页查询实现public ListUser getUsers(int page, int size) { return allUsers.stream() .skip((page - 1) * size) .limit(size) .collect(Collectors.toList()); }8. 常见问题排查8.1 Stream已关闭错误StreamString stream list.stream(); stream.forEach(System.out::println); stream.count(); // 抛出IllegalStateException8.2 空指针异常// 安全处理可能为null的集合 StreamString safeStream Optional.ofNullable(list) .orElse(Collections.emptyList()) .stream();8.3 并行流中的线程安全问题ListString result Collections.synchronizedList(new ArrayList()); list.parallelStream().forEach(result::add); // 仍然不安全 // 正确做法 ListString safeResult list.parallelStream() .collect(Collectors.toList());8.4 性能不达预期检查是否真的需要并行流避免在流操作中使用synchronized使用原始类型流IntStream等减少装箱开销9. Java 16 新特性9.1 Stream.toList()ListString list stream.toList(); // 比collect(Collectors.toList())更简洁9.2 Stream.mapMulti()ListNumber numbers ...; ListInteger integers numbers.stream() .mapMulti((number, consumer) - { if (number instanceof Integer i) { consumer.accept(i); } }) .toList();9.3 Stream.iterate() 增强// 可以添加谓词条件 Stream.iterate(0, n - n 100, n - n 1) .forEach(System.out::println);经过多年实践我发现Stream API最强大的地方在于它让代码更清晰地表达了业务逻辑。曾经需要仔细阅读的复杂循环现在通过流操作可以一目了然。但也要注意不是所有场景都适合用Stream - 对于简单的遍历传统的for循环可能更直观。关键是要在可读性和功能性之间找到平衡点。