关于Stream

在 Java 中,Stream API 是 Java 8 引入的一个强大工具,用于处理集合(Collection)中的元素。它提供了一种高效且声明式的方式来处理数据,支持过滤、映射、聚合等操作。Stream 操作可以分为两类:中间操作(Intermediate Operations)终止操作(Terminal Operations)

Stream 的基本概念

  • 流(Stream):不是一个数据结构,而是一个来自数据源的元素队列并支持聚合操作。
  • 数据源:可以是集合、数组、I/O 通道等。
  • 聚合操作:如 filter、map、reduce、find、match 等。
  • 特点
    • 不存储数据:流只是对数据源的元素进行计算。
    • 一次性使用:流只能遍历一次,遍历后就会关闭。
    • 延迟执行:中间操作不会立即执行,只有在终止操作时才会触发。

中间操作(Intermediate Operations)

中间操作会返回一个新的流,允许你链式调用多个操作。常见的中间操作包括:

1. 过滤(Filtering)
  • filter(Predicate<T>) :过滤出满足条件的元素。

    java 复制代码
    List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
    names.stream()
         .filter(name -> name.length() > 4)  // 保留长度大于4的名字
         .forEach(System.out::println);     // 输出:Alice, Charlie
2. 映射(Mapping)
  • map(Function<T, R>) :将元素转换为另一种类型。

    java 复制代码
    List<Integer> lengths = names.stream()
                                 .map(String::length)  // 将每个字符串映射为其长度
                                 .collect(Collectors.toList());  // [5, 3, 7, 5]
  • flatMap(Function<T, Stream<R>>) :将每个元素的流合并为一个流。

    java 复制代码
    List<List<Integer>> nestedList = Arrays.asList(
        Arrays.asList(1, 2),
        Arrays.asList(3, 4)
    );
    List<Integer> flattenedList = nestedList.stream()
                                            .flatMap(List::stream)  // 将嵌套列表展平
                                            .collect(Collectors.toList());  // [1, 2, 3, 4]
3. 排序(Sorting)
  • sorted():自然排序。

  • sorted(Comparator<T>) :自定义排序。

    java 复制代码
    names.stream()
         .sorted()  // 按字母顺序排序
         .forEach(System.out::println);
    
    names.stream()
         .sorted(Comparator.comparingInt(String::length))  // 按长度排序
         .forEach(System.out::println);
4. 去重(Distinct)
  • distinct() :根据元素的 equals() 方法去重。

    java 复制代码
    List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 3, 3);
    numbers.stream()
           .distinct()  // 去重
           .forEach(System.out::println);  // 输出:1, 2, 3
5. 截断(Limiting)和跳过(Skipping)
  • limit(long n):截取前 n 个元素。

  • skip(long n) :跳过前 n 个元素。

    java 复制代码
    names.stream()
         .limit(2)  // 只取前两个元素
         .forEach(System.out::println);  // 输出:Alice, Bob
    
    names.stream()
         .skip(2)  // 跳过前两个元素
         .forEach(System.out::println);  // 输出:Charlie, David
6. ** peek**
  • peek(Consumer<T>) :对每个元素执行操作,但不改变元素,常用于调试。

    java 复制代码
    names.stream()
         .filter(name -> name.length() > 4)
         .peek(name -> System.out.println("Filtered name: " + name))  // 调试输出
         .map(String::toUpperCase)
         .forEach(System.out::println);

终止操作(Terminal Operations)

终止操作会消耗流,产生一个结果或副作用。常见的终止操作包括:

1. 遍历(ForEach)
  • forEach(Consumer<T>) :对每个元素执行操作。

    java 复制代码
    names.stream().forEach(name -> System.out.println("Hello, " + name));
2. 收集(Collect)
  • collect(Collector<T, A, R>) :将元素收集到集合或其他数据结构中。

    java 复制代码
    List<String> filteredNames = names.stream()
                                      .filter(name -> name.startsWith("A"))
                                      .collect(Collectors.toList());
    
    Map<Integer, List<String>> namesByLength = names.stream()
                                                    .collect(Collectors.groupingBy(String::length));
3. 聚合(Reduce)
  • reduce(T identity, BinaryOperator<T>) :将元素组合成一个值。

    java 复制代码
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
    int sum = numbers.stream()
                    .reduce(0, Integer::sum);  // 计算总和:15
4. 匹配(Matching)
  • anyMatch(Predicate<T>):是否至少有一个元素匹配。

  • allMatch(Predicate<T>):是否所有元素都匹配。

  • noneMatch(Predicate<T>) :是否没有元素匹配。

    java 复制代码
    boolean hasLongName = names.stream().anyMatch(name -> name.length() > 5);  // true
    boolean allUpperCase = names.stream().allMatch(name -> name.equals(name.toUpperCase()));  // false
5. 查找(Finding)
  • findFirst():返回第一个元素。

  • findAny() :返回任意一个元素(并行流中更有用)。

    java 复制代码
    Optional<String> first = names.stream().findFirst();
    Optional<String> any = names.parallelStream().findAny();
6. 计数(Count)
  • count() :返回元素数量。

    java 复制代码
    long count = names.stream().filter(name -> name.startsWith("C")).count();  // 1
7. 最大值和最小值
  • max(Comparator<T>)min(Comparator<T>) :返回最大/最小值。

    java 复制代码
    Optional<String> longestName = names.stream()
                                       .max(Comparator.comparingInt(String::length));

并行流(Parallel Stream)

Stream 支持并行处理,通过 parallelStream()stream().parallel() 创建并行流:

java 复制代码
long count = names.parallelStream()  // 并行流
                 .filter(name -> name.length() > 4)
                 .count();
  • 注意:并行流适用于计算密集型且无状态的操作,否则可能导致线程安全问题。

示例:综合应用

java 复制代码
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David", "Eve");

int sumOfLengths = names.stream()
                       .filter(name -> name.length() > 3)  // 过滤长度大于3的名字
                       .map(String::length)  // 映射为长度
                       .reduce(0, Integer::sum);  // 求和

System.out.println("Sum of lengths: " + sumOfLengths);  // 输出:22

Stream 操作的特点

  1. 延迟执行:中间操作不会立即执行,只有在终止操作时才会触发。
  2. 一次性使用:流只能遍历一次,遍历后不能再次使用。
  3. 内部迭代:Stream 操作由库内部实现迭代,比外部迭代(如 for 循环)更简洁。

总结

Stream API 通过中间操作和终止操作的组合,提供了一种高效、灵活且易读的方式来处理集合数据。掌握这些操作可以让你的代码更简洁、更具表现力。

相关推荐
Blossom.1183 分钟前
基于深度学习的图像分类:使用Capsule Networks实现高效分类
人工智能·python·深度学习·神经网络·机器学习·分类·数据挖掘
CodeCraft Studio10 分钟前
借助Aspose.HTML控件,在 Python 中将 HTML 转换为 Markdown
开发语言·python·html·markdown·aspose·html转markdown·asposel.html
QQ_43766431411 分钟前
C++11 右值引用 Lambda 表达式
java·开发语言·c++
永卿00111 分钟前
设计模式-迭代器模式
java·设计模式·迭代器模式
aramae12 分钟前
大话数据结构之<队列>
c语言·开发语言·数据结构·算法
誰能久伴不乏19 分钟前
Linux如何执行系统调用及高效执行系统调用:深入浅出的解析
java·服务器·前端
悠哉悠哉愿意30 分钟前
【电赛学习笔记】MaxiCAM 项目实践——与单片机的串口通信
笔记·python·单片机·嵌入式硬件·学习·视觉检测
封奚泽优34 分钟前
使用Python实现单词记忆软件
开发语言·python·random·qpushbutton·qtwidgets·qtcore·qtgui
慕y27441 分钟前
Java学习第七十二部分——Zookeeper
java·学习·java-zookeeper
midsummer_woo1 小时前
基于spring boot的医院挂号就诊系统(源码+论文)
java·spring boot·后端