Java Stream 流的常见用法和场景,以及注意事项

一、Stream 流的概述

Java Stream 是 Java 8 引入的一个重要的功能,它允许你以声明性的方式对集合或其他数据源进行操作。Stream 流提供了一种高效、易读且功能强大的方式来处理数据集合,支持顺序和并行两种处理方式。

二、Stream 流的常见用法
1. 创建 Stream 流
// 从集合创建
List list = Arrays.asList("a", "b", "c");
Stream stream = list.stream();

// 从数组创建
String[] array = new String[]{"a", "b", "c"};
Stream streamFromArray = Arrays.stream(array);

// 使用静态方法 Stream.of()
Stream streamOf = Stream.of("a", "b", "c");
2. 筛选过滤
// filter() 方法用于筛选符合条件的元素
List numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
List evenNumbers = numbers.stream()
    .filter(n -> n % 2 == 0)
    .collect(Collectors.toList());
// evenNumbers 结果为 [2, 4, 6]
3. 映射转换
// map() 方法用于将流中的元素映射为另一种类型
List words = Arrays.asList("hello", "world", "java");
List lengths = words.stream()
    .map(String::length)
    .collect(Collectors.toList());
// lengths 结果为 [5, 5, 4]
4. 聚合操作
// 使用 reduce() 进行聚合操作
List numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()
    .reduce(0, (a, b) -> a + b);
// sum 结果为 15

// 使用收集器 Collectors 进行聚合
List numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()
    .collect(Collectors.summingInt(n -> n));
// sum 结果为 15
5. 排序
// sorted() 方法对流中的元素进行排序
List numbers = Arrays.asList(5, 3, 2, 4, 1);
List sortedNumbers = numbers.stream()
    .sorted()
    .collect(Collectors.toList());
// sortedNumbers 结果为 [1, 2, 3, 4, 5]

// 自定义排序
List words = Arrays.asList("banana", "apple", "orange");
List sortedWords = words.stream()
    .sorted((a, b) -> a.length() - b.length())
    .collect(Collectors.toList());
// sortedWords 结果为 ["apple", "banana", "orange"]
6. 去重
// distinct() 方法用于去除重复的元素
List numbers = Arrays.asList(1, 2, 2, 3, 3, 3, 4);
List uniqueNumbers = numbers.stream()
    .distinct()
    .collect(Collectors.toList());
// uniqueNumbers 结果为 [1, 2, 3, 4]
7. 限制和跳过
// limit() 方法限制流中元素的数量
List numbers = Arrays.asList(1, 2, 3, 4, 5);
List limitedNumbers = numbers.stream()
    .limit(3)
    .collect(Collectors.toList());
// limitedNumbers 结果为 [1, 2, 3]

// skip() 方法跳过流中的前 n 个元素
List numbers = Arrays.asList(1, 2, 3, 4, 5);
List skippedNumbers = numbers.stream()
    .skip(2)
    .collect(Collectors.toList());
// skippedNumbers 结果为 [3, 4, 5]
8. 匹配和查找
// anyMatch() 检查是否至少有一个元素符合给定的条件
List words = Arrays.asList("hello", "world", "java");
boolean hasLongWord = words.stream()
    .anyMatch(word -> word.length() > 5);
// hasLongWord 结果为 true

// allMatch() 检查所有元素是否都符合给定的条件
List numbers = Arrays.asList(2, 4, 6, 8);
boolean allEven = numbers.stream()
    .allMatch(n -> n % 2 == 0);
// allEven 结果为 true

// noneMatch() 检查没有元素符合给定的条件
List numbers = Arrays.asList(1, 3, 5, 7);
boolean noneEven = numbers.stream()
    .noneMatch(n -> n % 2 == 0);
// noneEven 结果为 true

// findFirst() 返回流中的第一个元素
List words = Arrays.asList("hello", "world", "java");
Optional firstWord = words.stream()
    .findFirst();
// firstWord 结果为 Optional["hello"]

// findAny() 返回流中的任意一个元素(在并行流中可能更高效)
List words = Arrays.asList("hello", "world", "java");
Optional anyWord = words.stream()
    .findAny();
// anyWord 结果可能为 Optional["hello"] 或其他元素
9. 流的扁平化
// flatMap() 将流中的每个元素映射为另一个流,然后将这些流连接成一个流
List> listOfLists = Arrays.asList(
    Arrays.asList(1, 2),
    Arrays.asList(3, 4),
    Arrays.asList(5, 6)
);
List flatList = listOfLists.stream()
    .flatMap(List::stream)
    .collect(Collectors.toList());
// flatList 结果为 [1, 2, 3, 4, 5, 6]
10. 转换为其他集合形式
// 转换为 List
List numbers = Arrays.asList(1, 2, 3, 4, 5);
List squaredList = numbers.stream()
    .map(n -> n * n)
    .collect(Collectors.toList());
// squaredList 结果为 [1, 4, 9, 16, 25]

// 转换为 Set
List numbers = Arrays.asList(1, 2, 2, 3, 3, 3, 4);
Set uniqueSet = numbers.stream()
    .collect(Collectors.toSet());
// uniqueSet 结果为 {1, 2, 3, 4}

// 转换为 Map
List words = Arrays.asList("hello", "world", "java");
Map wordLengthMap = words.stream()
    .collect(Collectors.toMap(word -> word, word -> word.length()));
// wordLengthMap 结果为 {"hello"=5, "world"=5, "java"=4}
三、Stream 流的常见场景
1. 数据处理
// 计算员工工资总和
List employees = Arrays.asList(
    new Employee("Alice", 5000),
    new Employee("Bob", 6000),
    new Employee("Charlie", 7000)
);
int totalSalary = employees.stream()
    .mapToInt(Employee::getSalary)
    .sum();
// totalSalary 结果为 18000
2. 集合操作优化
// 找出最长的单词
List words = Arrays.asList("hello", "world", "java");
String longestWord = words.stream()
    .max((a, b) -> a.length() - b.length())
    .orElse("");
// longestWord 结果为 "hello"
3. 函数式编程实践
// 使用函数式接口对数字进行平方操作
Function square = x -> x * x;
List numbers = Arrays.asList(1, 2, 3, 4, 5);
List squaredNumbers = numbers.stream()
    .map(square)
    .collect(Collectors.toList());
// squaredNumbers 结果为 [1, 4, 9, 16, 25]
四、Stream 流的注意事项
1. 避免在 Stream 操作中产生副作用
// 不推荐:在 Stream 操作中修改外部变量
List numbers = Arrays.asList(1, 2, 3, 4, 5);
List squaredList = new ArrayList<>();
numbers.stream()
    .forEach(n -> squaredList.add(n * n));
// squaredList 结果为 [1, 4, 9, 16, 25]

// 推荐:使用 map() 和 collect() 进行无副作用的操作
List squaredList = numbers.stream()
    .map(n -> n * n)
    .collect(Collectors.toList());
2. 注意 Stream 的延迟执行特性
// Stream 操作是延迟执行的,只有在终止操作时才会真正执行
List numbers = Arrays.asList(1, 2, 3, 4, 5);
Stream stream = numbers.stream()
    .filter(n -> n % 2 == 0);
// 此时还没有执行过滤操作

List evenNumbers = stream.collect(Collectors.toList());
// 此时才真正执行过滤操作
3. 处理异常和错误
// 在 Stream 操作中处理异常
List words = Arrays.asList("hello", "world", "java");
List lengths = words.stream()
    .map(word -> {
        try {
            return word.length();
        } catch (Exception e) {
            return 0;
        }
    })
    .collect(Collectors.toList());
4. 注意 Stream 的不可重复使用性
// Stream 只能使用一次,再次使用会抛出异常
List numbers = Arrays.asList(1, 2, 3, 4, 5);
Stream stream = numbers.stream();
stream.filter(n -> n % 2 == 0).collect(Collectors.toList());
// stream.filter(n -> n % 2 == 0).collect(Collectors.toList()); // 再次使用会抛出异常
5. 注意并行流的使用
// 并行流可以提高处理性能,但需要注意线程安全和顺序问题
List numbers = Arrays.asList(1, 2, 3, 4, 5);
List squaredList = numbers.parallelStream()
    .map(n -> n * n)
    .collect(Collectors.toList());
// squaredList 结果可能为 [1, 4, 9, 16, 25],但顺序可能不保证
6. 避免过度使用 Stream
// 对于简单操作,传统的循环可能更高效
List numbers = Arrays.asList(1, 2, 3, 4, 5);
// 不推荐:过度使用 Stream
List squaredList = numbers.stream()
    .map(n -> n * n)
    .collect(Collectors.toList());
// 推荐:对于简单操作,直接使用循环
List squaredList = new ArrayList<>();
for (int n : numbers) {
    squaredList.add(n * n);
}
7. 注意空指针异常
// 在 Stream 操作中处理可能的空值
List words = Arrays.asList("hello", null, "java");
List nonNullWords = words.stream()
    .filter(Objects::nonNull)
    .collect(Collectors.toList());
// nonNullWords 结果为 ["hello", "java"]
8. 注意收集器的使用
// 使用收集器时注意线程安全和性能问题
List numbers = Arrays.asList(1, 2, 3, 4, 5);
Map> partitionedMap = numbers.stream()
    .collect(Collectors.partitioningBy(n -> n % 2 == 0));
// partitionedMap 结果为 {false=[1, 3, 5], true=[2, 4]}
9. 注意流的中间操作和终止操作
// 中间操作不会执行任何处理,直到遇到终止操作
List numbers = Arrays.asList(1, 2, 3, 4, 5);
Stream stream = numbers.stream()
    .filter(n -> n % 2 == 0);
// 此时还没有执行过滤操作

List evenNumbers = stream.collect(Collectors.toList());
// 此时才真正执行过滤操作
10. 注意流的源数据不可变性
// Stream 操作不会修改源数据,除非显式地进行修改
List numbers = Arrays.asList(1, 2, 3, 4, 5);
List squaredList = numbers.stream()
    .map(n -> n * n)
    .collect(Collectors.toList());
// numbers 列表保持不变,squaredList 是新的列表

总结

Java Stream 流提供了一种高效、易读且功能强大的方式来处理数据集合。通过筛选过滤、映射转换、聚合操作、排序、去重、限制和跳过、匹配和查找、流的扁平化以及转换为其他集合形式等常见用法,可以方便地对数据进行各种操作。在实际应用中,需要注意避免副作用、处理异常、注意延迟执行特性、避免过度使用 Stream 等事项,以确保代码的正确性和高效性。

你可能感兴趣的:(java,windows)