Java Stream 流

42 阅读5分钟

一、一句话总结

Stream = 数据流管道,支持函数式操作,可链式调用,延迟执行,用于高效处理集合数据

二、核心概念

数据源 → 创建Stream → 中间操作(过滤/映射) → 终端操作(收集/计算)
           ↑                    ↑                  ↑
        stream()          filter/map/sorted     collect/count

三、创建Stream

1. 从集合创建

// 1. Collection接口的stream()方法
List<String> list = Arrays.asList("A", "B", "C");
Stream<String> stream1 = list.stream();           // 顺序流
Stream<String> parallelStream = list.parallelStream(); // 并行流

// 2. 数组创建
String[] array = {"A", "B", "C"};
Stream<String> stream2 = Arrays.stream(array);
Stream<String> stream3 = Stream.of(array);
Stream<String> stream4 = Stream.of("A", "B", "C");

2. 其他方式创建

// 3. 无限流
Stream<Integer> infinite1 = Stream.iterate(0, n -> n + 1); // 0,1,2,3...
Stream<Double> infinite2 = Stream.generate(Math::random);  // 随机数流

// 4. 范围流
IntStream range1 = IntStream.range(1, 5);    // 1,2,3,4 (不包括5)
IntStream range2 = IntStream.rangeClosed(1, 5); // 1,2,3,4,5

// 5. 空流
Stream<String> emptyStream = Stream.empty();

四、中间操作(返回新Stream)

1. 筛选过滤

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);

// filter - 过滤
List<Integer> even = numbers.stream()
    .filter(n -> n % 2 == 0)          // 保留偶数
    .collect(Collectors.toList());    // [2, 4, 6]

// distinct - 去重
List<Integer> distinct = Arrays.asList(1, 2, 2, 3, 3, 3).stream()
    .distinct()                       // 去重
    .collect(Collectors.toList());    // [1, 2, 3]

// limit - 限制数量
List<Integer> first3 = numbers.stream()
    .limit(3)                         // 取前3个
    .collect(Collectors.toList());    // [1, 2, 3]

// skip - 跳过前N个
List<Integer> skip2 = numbers.stream()
    .skip(2)                          // 跳过前2个
    .collect(Collectors.toList());    // [3, 4, 5, 6]

2. 映射转换

List<String> words = Arrays.asList("hello", "world");

// map - 一对一转换
List<Integer> lengths = words.stream()
    .map(String::length)              // String → Integer
    .collect(Collectors.toList());    // [5, 5]

List<String> upper = words.stream()
    .map(String::toUpperCase)         // 转大写
    .collect(Collectors.toList());    // [HELLO, WORLD]

// flatMap - 一对多展开
List<List<String>> listOfLists = Arrays.asList(
    Arrays.asList("a", "b"),
    Arrays.asList("c", "d")
);
List<String> flattened = listOfLists.stream()
    .flatMap(List::stream)            // 展开为单个流
    .collect(Collectors.toList());    // [a, b, c, d]

3. 排序

List<String> names = Arrays.asList("Tom", "Jerry", "Alice", "Bob");

// 自然排序
List<String> sorted = names.stream()
    .sorted()                         // 自然顺序
    .collect(Collectors.toList());    // [Alice, Bob, Jerry, Tom]

// 自定义排序
List<String> customSorted = names.stream()
    .sorted(Comparator.reverseOrder()) // 逆序
    .collect(Collectors.toList());    // [Tom, Jerry, Bob, Alice]

// 多条件排序
List<Person> people = Arrays.asList(
    new Person("Alice", 25),
    new Person("Bob", 30),
    new Person("Alice", 20)
);
List<Person> sortedPeople = people.stream()
    .sorted(Comparator
        .comparing(Person::getName)
        .thenComparing(Person::getAge)) // 先按姓名,再按年龄
    .collect(Collectors.toList());

五、终端操作(返回结果)

1. 收集结果

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

// 收集为List
List<String> list = names.stream()
    .filter(n -> n.length() > 3)
    .collect(Collectors.toList());          // [Alice, Charlie]

// 收集为Set(自动去重)
Set<String> set = names.stream()
    .collect(Collectors.toSet());

// 收集为Map
Map<String, Integer> map = names.stream()
    .collect(Collectors.toMap(
        name -> name,                        // key
        String::length                       // value
    ));                                      // {Alice=5, Bob=3, Charlie=7}

// 连接字符串
String joined = names.stream()
    .collect(Collectors.joining(", "));     // "Alice, Bob, Charlie"

2. 统计计算

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

// 计数
long count = numbers.stream().count();      // 5

// 求和
int sum = numbers.stream()
    .mapToInt(Integer::intValue)
    .sum();                                 // 15

// 平均值
OptionalDouble avg = numbers.stream()
    .mapToInt(Integer::intValue)
    .average();                             // OptionalDouble[3.0]

// 最大值/最小值
Optional<Integer> max = numbers.stream()
    .max(Integer::compare);                 // Optional[5]
Optional<Integer> min = numbers.stream()
    .min(Integer::compare);                 // Optional[1]

// 归约(reduce)
Optional<Integer> sumReduce = numbers.stream()
    .reduce((a, b) -> a + b);              // Optional[15]
Integer sumWithInit = numbers.stream()
    .reduce(0, (a, b) -> a + b);           // 15(有初始值)

3. 匹配查找

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

// 是否存在满足条件的元素
boolean anyEven = numbers.stream()
    .anyMatch(n -> n % 2 == 0);            // true(存在偶数)

// 是否所有元素都满足条件
boolean allPositive = numbers.stream()
    .allMatch(n -> n > 0);                 // true(都大于0)

// 是否没有元素满足条件
boolean noneNegative = numbers.stream()
    .noneMatch(n -> n < 0);                // true(没有负数)

// 查找第一个
Optional<Integer> firstEven = numbers.stream()
    .filter(n -> n % 2 == 0)
    .findFirst();                           // Optional[2]

// 查找任意一个(并行流中效率更高)
Optional<Integer> anyEven2 = numbers.parallelStream()
    .filter(n -> n % 2 == 0)
    .findAny();                            // 任意一个偶数

六、分组和分区

1. 分组

List<Person> people = Arrays.asList(
    new Person("Alice", 25, "IT"),
    new Person("Bob", 30, "HR"),
    new Person("Charlie", 25, "IT"),
    new Person("David", 35, "HR")
);

// 按部门分组
Map<String, List<Person>> byDept = people.stream()
    .collect(Collectors.groupingBy(Person::getDepartment));
// {IT=[Alice, Charlie], HR=[Bob, David]}

// 分组后统计
Map<String, Long> countByDept = people.stream()
    .collect(Collectors.groupingBy(
        Person::getDepartment,
        Collectors.counting()
    ));                                   // {IT=2, HR=2}

// 多级分组
Map<String, Map<Integer, List<Person>>> nestedGroup = people.stream()
    .collect(Collectors.groupingBy(
        Person::getDepartment,
        Collectors.groupingBy(Person::getAge)
    ));

2. 分区

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);

// 按条件分为true/false两组
Map<Boolean, List<Integer>> partitioned = numbers.stream()
    .collect(Collectors.partitioningBy(n -> n % 2 == 0));
// {false=[1, 3, 5], true=[2, 4, 6]}

七、并行流

1. 创建并行流

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

// 方法1:从集合创建
Stream<Integer> parallelStream = numbers.parallelStream();

// 方法2:转换顺序流为并行流
Stream<Integer> parallel = numbers.stream().parallel();

// 方法3:转换并行流为顺序流
Stream<Integer> sequential = parallelStream.sequential();

2. 并行流注意事项

// 适合并行的情况:大数据量、计算密集、无状态操作
long sum = numbers.parallelStream()
    .mapToLong(Integer::longValue)
    .sum();

// 不适合并行的情况:
// 1. 有状态操作(sorted可能有问题)
// 2. 操作顺序重要时
// 3. 数据量太小(并行开销可能更大)

八、实际应用示例

1. 数据处理管道

public class StreamExamples {
    
    // 示例1:统计单词频率
    public Map<String, Long> wordFrequency(String text) {
        return Arrays.stream(text.toLowerCase().split("\\W+"))
            .filter(word -> !word.isEmpty())
            .collect(Collectors.groupingBy(
                word -> word,
                Collectors.counting()
            ));
    }
    
    // 示例2:查询员工信息
    public List<Employee> findTopPaidEmployees(List<Employee> employees, int limit) {
        return employees.stream()
            .filter(e -> e.getDepartment().equals("IT"))
            .sorted(Comparator.comparing(Employee::getSalary).reversed())
            .limit(limit)
            .collect(Collectors.toList());
    }
    
    // 示例3:计算交易统计
    public TransactionStats analyzeTransactions(List<Transaction> transactions) {
        DoubleSummaryStatistics stats = transactions.stream()
            .mapToDouble(Transaction::getAmount)
            .summaryStatistics();
        
        return new TransactionStats(
            stats.getCount(),
            stats.getSum(),
            stats.getAverage(),
            stats.getMax(),
            stats.getMin()
        );
    }
}

2. 复杂数据转换

// 从订单列表生成报表
public class OrderReporter {
    
    public Report generateReport(List<Order> orders) {
        // 按用户分组,计算每个用户的订单总金额
        Map<String, Double> userTotal = orders.stream()
            .collect(Collectors.groupingBy(
                Order::getUserId,
                Collectors.summingDouble(Order::getTotalAmount)
            ));
        
        // 找出金额最高的订单
        Optional<Order> highestOrder = orders.stream()
            .max(Comparator.comparing(Order::getTotalAmount));
        
        // 按日期统计每日订单数
        Map<LocalDate, Long> dailyCount = orders.stream()
            .collect(Collectors.groupingBy(
                Order::getOrderDate,
                Collectors.counting()
            ));
        
        return new Report(userTotal, highestOrder, dailyCount);
    }
}

九、性能优化技巧

1. 短路操作

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

// 找到第一个长度大于5的名字后就停止
Optional<String> result = names.stream()
    .filter(name -> {
        System.out.println("检查: " + name);
        return name.length() > 5;
    })
    .findFirst();  // 只检查到Charlie就会停止

// 使用limit减少处理量
List<Integer> top3 = IntStream.range(1, 1000000)
    .filter(n -> n % 2 == 0)
    .limit(3)      // 只取前3个,避免处理全部
    .boxed()
    .collect(Collectors.toList());

2. 避免装箱拆箱

// ❌ 不好:频繁装箱拆箱
List<Integer> numbers = IntStream.range(1, 1000)
    .boxed()                      // 装箱
    .filter(n -> n % 2 == 0)
    .mapToInt(Integer::intValue)  // 拆箱
    .sum();

// ✅ 好:使用原始类型流
int sum = IntStream.range(1, 1000)
    .filter(n -> n % 2 == 0)      // 没有装箱拆箱
    .sum();

十、常见陷阱

1. 流只能消费一次

Stream<String> stream = Stream.of("A", "B", "C");

// 第一次消费
stream.forEach(System.out::println);  // 正常输出

// 第二次消费会报错
// stream.forEach(System.out::println);  // 抛出IllegalStateException

// 解决方案:重新创建流
Stream<String> newStream = Stream.of("A", "B", "C");
newStream.forEach(System.out::println);

2. 并行流的线程安全

// ❌ 线程不安全
List<Integer> result = new ArrayList<>();
IntStream.range(0, 1000).parallel()
    .forEach(result::add);  // 可能丢失数据或抛出异常

// ✅ 线程安全
List<Integer> safeResult = IntStream.range(0, 1000).parallel()
    .boxed()
    .collect(Collectors.toList());

总结要点

核心特性:

  1. 链式调用.stream().filter().map().collect()
  2. 延迟执行:只有终端操作才会真正执行
  3. 不可复用:流只能消费一次
  4. 并行处理.parallelStream()轻松并行

常用操作:

  • 中间操作:filter, map, sorted, distinct, limit, skip
  • 终端操作:collect, forEach, reduce, count, anyMatch
  • 收集器:Collectors.toList(), groupingBy(), joining()

选择指南:

  • 小数据量用顺序流
  • 大数据量、计算密集用并行流
  • 需要原始类型时用IntStream/LongStream/DoubleStream
  • 多次使用相同数据时重新创建流