一、Stream 基本概念
1.1 Stream 是什么
Java Stream 是 Java 8 引入的处理集合数据的 API,支持声明式编程和函数式操作。
// 传统方式 vs Stream 方式 List<String> names = Arrays.asList("Alice", "Bob", "Charlie"); // 传统方式 List<String> result = new ArrayList<>(); for (String name : names) { if (name.startsWith("A")) { result.add(name.toUpperCase()); } } // Stream 方式 List<String> result = names.stream() .filter(name -> name.startsWith("A")) .map(String::toUpperCase) .collect(Collectors.toList());二、Stream 创建方式
2.1 从集合创建
// 从 List 创建 List<String> list = Arrays.asList("a", "b", "c"); Stream<String> stream1 = list.stream(); Stream<String> parallelStream = list.parallelStream(); // 从 Set 创建 Set<Integer> set = new HashSet<>(Arrays.asList(1, 2, 3)); Stream<Integer> stream2 = set.stream();2.2 从数组创建
String[] array = {"a", "b", "c"}; Stream<String> stream = Arrays.stream(array); // 指定范围 Stream<String> rangeStream = Arrays.stream(array, 1, 3); // "b", "c"2.3 使用 Stream.of()
Stream<String> stream = Stream.of("a", "b", "c"); Stream<Integer> intStream = Stream.of(1, 2, 3);2.4 使用 Builder
Stream<String> stream = Stream.<String>builder() .add("a") .add("b") .add("c") .build();2.5 生成无限流
// 生成无限序列 Stream<Integer> infiniteStream = Stream.iterate(0, n -> n + 2) .limit(10); // 限制为前10个 // 生成随机数 Stream<Double> randomStream = Stream.generate(Math::random) .limit(5);2.6 基本类型 Stream
// IntStream IntStream intStream = IntStream.range(1, 10); // 1-9 IntStream closedIntStream = IntStream.rangeClosed(1, 10); // 1-10 // LongStream LongStream longStream = LongStream.range(1, 100); // DoubleStream DoubleStream doubleStream = DoubleStream.of(1.0, 2.0, 3.0);三、中间操作(Intermediate Operations)
3.1 filter() - 过滤
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David"); // 过滤长度大于3的名字 List<String> filtered = names.stream() .filter(name -> name.length() > 3) .collect(Collectors.toList()); // [Alice, Charlie, David] // 多条件过滤 List<String> multiFiltered = names.stream() .filter(name -> name.length() > 3) .filter(name -> name.startsWith("A")) .collect(Collectors.toList()); // [Alice]3.2 map() - 映射转换
// 转换大小写 List<String> upperCaseNames = names.stream() .map(String::toUpperCase) .collect(Collectors.toList()); // 提取对象属性 class Person { private String name; private int age; // getters, setters } List<Person> persons = Arrays.asList( new Person("Alice", 25), new Person("Bob", 30) ); List<String> personNames = persons.stream() .map(Person::getName) .collect(Collectors.toList()); // 复杂转换 List<Integer> nameLengths = names.stream() .map(String::length) .collect(Collectors.toList());3.3 flatMap() - 扁平化映射
// 将多个集合合并为一个流 List<List<String>> nestedList = Arrays.asList( Arrays.asList("a", "b"), Arrays.asList("c", "d") ); List<String> flatList = nestedList.stream() .flatMap(Collection::stream) .collect(Collectors.toList()); // [a, b, c, d] // 处理嵌套对象 class Order { private List<Item> items; // getter, setter } class Item { private String name; private double price; // getter, setter } List<Order> orders = Arrays.asList(order1, order2); List<Item> allItems = orders.stream() .flatMap(order -> order.getItems().stream()) .collect(Collectors.toList());3.4 distinct() - 去重
List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 3, 3, 4); List<Integer> distinctNumbers = numbers.stream() .distinct() .collect(Collectors.toList()); // [1, 2, 3, 4]3.5 sorted() - 排序
// 自然排序 List<String> sortedNames = names.stream() .sorted() .collect(Collectors.toList()); // 自定义排序 List<Person> sortedByAge = persons.stream() .sorted(Comparator.comparingInt(Person::getAge)) .collect(Collectors.toList()); // 多重排序 List<Person> multiSorted = persons.stream() .sorted(Comparator.comparing(Person::getName) .thenComparing(Person::getAge)) .collect(Collectors.toList()); // 反向排序 List<Person> reverseSorted = persons.stream() .sorted(Comparator.comparing(Person::getAge).reversed()) .collect(Collectors.toList());3.6 limit() 和 skip() - 限制和跳过
List<Integer> numbers = IntStream.rangeClosed(1, 10) .boxed() .collect(Collectors.toList()); // 获取前5个 List<Integer> firstFive = numbers.stream() .limit(5) .collect(Collectors.toList()); // [1, 2, 3, 4, 5] // 跳过前3个 List<Integer> skipThree = numbers.stream() .skip(3) .collect(Collectors.toList()); // [4, 5, 6, 7, 8, 9, 10] // 分页实现 int pageSize = 3; int pageNum = 2; // 第二页 List<Integer> page = numbers.stream() .skip((pageNum - 1) * pageSize) .limit(pageSize) .collect(Collectors.toList()); // [4, 5, 6]3.7 peek() - 调试查看
List<String> result = names.stream() .peek(System.out::println) // 调试输出 .filter(name -> name.length() > 3) .peek(name -> System.out.println("Filtered: " + name)) .collect(Collectors.toList());四、终端操作(Terminal Operations)
4.1 forEach() - 遍历
// 遍历输出 names.stream().forEach(System.out::println); // 并行流遍历(无序) names.parallelStream().forEach(System.out::println); // 并行流有序遍历 names.parallelStream().forEachOrdered(System.out::println);4.2 collect() - 收集
// 收集到List List<String> list = names.stream() .filter(n -> n.length() > 3) .collect(Collectors.toList()); // 收集到Set(自动去重) Set<String> set = names.stream() .collect(Collectors.toSet()); // 收集到特定集合 TreeSet<String> treeSet = names.stream() .collect(Collectors.toCollection(TreeSet::new)); // 收集到Map Map<String, Integer> nameLengthMap = names.stream() .collect(Collectors.toMap( Function.identity(), // key: 名字本身 String::length // value: 名字长度 )); // 处理key冲突 Map<String, Integer> mapWithConflict = names.stream() .collect(Collectors.toMap( Function.identity(), String::length, (oldValue, newValue) -> oldValue // 冲突时保留旧值 ));4.3 toArray() - 转换为数组
String[] array = names.stream().toArray(String[]::new); // 基本类型数组 int[] intArray = IntStream.range(1, 5).toArray();4.4 reduce() - 归约
// 求和 Optional<Integer> sum = Stream.of(1, 2, 3, 4, 5) .reduce(Integer::sum); // 或 int sumResult = Stream.of(1, 2, 3, 4, 5) .reduce(0, Integer::sum); // 求最大值 Optional<Integer> max = Stream.of(1, 2, 3, 4, 5) .reduce(Integer::max); // 字符串连接 String concatenated = Stream.of("a", "b", "c") .reduce("", String::concat); // 复杂对象归约 class Order { private List<Item> items; // getter, setter } double totalPrice = orders.stream() .flatMap(order -> order.getItems().stream()) .map(Item::getPrice) .reduce(0.0, Double::sum);4.5 匹配操作
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); // 任意匹配 boolean anyMatch = numbers.stream() .anyMatch(n -> n > 3); // true // 全部匹配 boolean allMatch = numbers.stream() .allMatch(n -> n > 0); // true // 无匹配 boolean noneMatch = numbers.stream() .noneMatch(n -> n < 0); // true4.6 查找操作
Optional<String> first = names.stream() .filter(n -> n.startsWith("A")) .findFirst(); // 返回第一个元素 Optional<String> any = names.stream() .filter(n -> n.startsWith("A")) .findAny(); // 并行流中效率更高4.7 统计操作
// 计数 long count = names.stream().count(); // 使用 IntStream 统计 IntSummaryStatistics stats = IntStream.range(1, 100) .summaryStatistics(); System.out.println("Count: " + stats.getCount()); System.out.println("Sum: " + stats.getSum()); System.out.println("Min: " + stats.getMin()); System.out.println("Max: " + stats.getMax()); System.out.println("Average: " + stats.getAverage());五、Collectors 高级用法
5.1 分组
List<Person> persons = Arrays.asList( new Person("Alice", 25, "New York"), new Person("Bob", 30, "New York"), new Person("Charlie", 25, "London") ); // 按年龄分组 Map<Integer, List<Person>> groupByAge = persons.stream() .collect(Collectors.groupingBy(Person::getAge)); // 多级分组 Map<Integer, Map<String, List<Person>>> multiGroup = persons.stream() .collect(Collectors.groupingBy( Person::getAge, Collectors.groupingBy(Person::getCity) )); // 分组并计数 Map<Integer, Long> ageCount = persons.stream() .collect(Collectors.groupingBy( Person::getAge, Collectors.counting() )); // 分组并求平均值 Map<Integer, Double> averageAgeByCity = persons.stream() .collect(Collectors.groupingBy( Person::getAge, Collectors.averagingInt(Person::getAge) ));5.2 分区
// 按条件分区 Map<Boolean, List<Person>> partition = persons.stream() .collect(Collectors.partitioningBy(p -> p.getAge() > 25)); // 分区并统计 Map<Boolean, Long> partitionCount = persons.stream() .collect(Collectors.partitioningBy( p -> p.getAge() > 25, Collectors.counting() ));5.3 连接字符串
// 简单连接 String joined = names.stream() .collect(Collectors.joining()); // "AliceBobCharlie" // 带分隔符 String joinedWithComma = names.stream() .collect(Collectors.joining(", ")); // "Alice, Bob, Charlie" // 带前缀和后缀 String joinedFull = names.stream() .collect(Collectors.joining(", ", "[", "]")); // "[Alice, Bob, Charlie]"5.4 统计汇总
// 求总和 int totalAge = persons.stream() .collect(Collectors.summingInt(Person::getAge)); // 求平均值 double averageAge = persons.stream() .collect(Collectors.averagingInt(Person::getAge)); // 综合统计 IntSummaryStatistics ageStats = persons.stream() .collect(Collectors.summarizingInt(Person::getAge));六、并行流(Parallel Stream)
6.1 创建并行流
// 从集合创建并行流 List<String> names = Arrays.asList("Alice", "Bob", "Charlie"); Stream<String> parallelStream = names.parallelStream(); // 将顺序流转为并行流 Stream<String> parallel = names.stream().parallel(); // 将并行流转为顺序流 Stream<String> sequential = parallelStream.sequential();6.2 并行流注意事项
// 非线程安全操作(错误示例) List<Integer> unsafeList = new ArrayList<>(); IntStream.range(0, 10000).parallel() .forEach(unsafeList::add); // 可能丢失数据 // 线程安全操作(正确示例) List<Integer> safeList = IntStream.range(0, 10000).parallel() .boxed() .collect(Collectors.toList()); // 并行流排序 List<Integer> sortedParallel = IntStream.range(0, 10000).parallel() .sorted() .boxed() .collect(Collectors.toList());6.3 并行流性能考虑
// 小数据量顺序流更快 List<Integer> smallList = IntStream.range(0, 100) .boxed().collect(Collectors.toList()); long start = System.currentTimeMillis(); smallList.stream().map(i -> i * 2).count(); long sequentialTime = System.currentTimeMillis() - start; start = System.currentTimeMillis(); smallList.parallelStream().map(i -> i * 2).count(); long parallelTime = System.currentTimeMillis() - start; System.out.println("Sequential: " + sequentialTime + "ms"); System.out.println("Parallel: " + parallelTime + "ms");七、实战案例
7.1 数据处理管道
public class StreamPipelineExample { public static void main(String[] args) { List<Transaction> transactions = Arrays.asList( new Transaction("USD", 1000.0, Transaction.Type.DEPOSIT), new Transaction("USD", 500.0, Transaction.Type.WITHDRAWAL), new Transaction("EUR", 800.0, Transaction.Type.DEPOSIT), new Transaction("USD", 300.0, Transaction.Type.DEPOSIT), new Transaction("JPY", 10000.0, Transaction.Type.WITHDRAWAL) ); // 复杂数据处理管道 Map<String, Double> totalDepositsByCurrency = transactions.stream() .filter(t -> t.getType() == Transaction.Type.DEPOSIT) .filter(t -> t.getAmount() > 200) .collect(Collectors.groupingBy( Transaction::getCurrency, Collectors.summingDouble(Transaction::getAmount) )); totalDepositsByCurrency.forEach((currency, total) -> System.out.println(currency + ": " + total)); } }7.2 文件处理
public class FileStreamExample { public static void main(String[] args) throws IOException { // 读取文件并处理 List<String> lines = Files.lines(Paths.get("data.txt")) .filter(line -> !line.trim().isEmpty()) .map(String::toUpperCase) .sorted() .collect(Collectors.toList()); // 写入文件 Files.write(Paths.get("output.txt"), lines); } }7.3 数据库查询模拟
public class DatabaseStreamExample { public static List<User> findActiveUsers(List<User> users) { return users.stream() .filter(User::isActive) .filter(user -> user.getLastLogin().isAfter(LocalDate.now().minusDays(30))) .sorted(Comparator.comparing(User::getLastLogin).reversed()) .collect(Collectors.toList()); } }八、性能优化建议
8.1 使用基本类型流
// 避免装箱拆箱开销 // 不好 List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); int sum = numbers.stream() .mapToInt(Integer::intValue) // 需要拆箱 .sum(); // 好 int sum = IntStream.rangeClosed(1, 5).sum();8.2 短路操作优化
// 使用短路操作提前结束 boolean hasMatch = largeList.stream() .filter(this::expensiveOperation) .anyMatch(this::condition); // 找到第一个匹配就停止8.3 避免重复计算
// 不好 - 重复计算 List<String> result = names.stream() .filter(name -> expensiveCheck(name)) .map(name -> transform(name)) .filter(transformed -> expensiveCheck(transformed)) .collect(Collectors.toList()); // 好 - 缓存中间结果 List<String> filtered = names.stream() .filter(name -> expensiveCheck(name)) .collect(Collectors.toList()); List<String> result = filtered.stream() .map(name -> transform(name)) .filter(transformed -> expensiveCheck(transformed)) .collect(Collectors.toList());九、常见陷阱
9.1 Stream 只能消费一次
Stream<String> stream = names.stream(); stream.forEach(System.out::println); // stream.count(); // 错误!Stream 已经关闭9.2 空指针处理
// 使用 Optional 避免空指针 Optional<String> first = names.stream() .filter(Objects::nonNull) .findFirst(); // 或者在创建 Stream 时处理 Stream<String> safeStream = Optional.ofNullable(names) .orElse(Collections.emptyList()) .stream();9.3 修改外部状态
// 避免副作用 List<String> result = new ArrayList<>(); names.stream() .forEach(result::add); // 不好 // 使用 collect List<String> goodResult = names.stream() .collect(Collectors.toList()); // 好总结
Java Stream 提供了强大、灵活的数据处理能力,核心要点:
创建流:多种创建方式,灵活选择
中间操作:链式调用,延迟执行
终端操作:触发计算,产生结果
并行流:合理使用提升性能
Collectors:强大的结果收集工具
在实际开发中,应根据具体场景选择合适的流操作,注意性能优化,避免常见陷阱。