Java Stream API 详解

Java Stream API 详解

1. 什么是 Stream API?

Stream API 是 Java 8 引入的一种用于处理集合(如数组、列表)的强大工具。它提供了一种声明性方式处理数据,可以简化代码并提高可读性。Stream 不是数据结构,它只是一种从支持的数据源(如集合、数组等)中提取的元素序列。

组成部分:
  • 数据源:任何可以提供数据的地方,例如集合、数组等。
  • 操作链:一系列中间和终端操作用于处理数据。
  • 最终结果:经过流操作的结果,可能是一个值、一个集合、或根本不返回结果(如打印)。
2. Stream API 的特性
  • 声明性:通过函数式编程风格处理数据,而不是使用传统的循环等命令式编程。

    例子:不用写 for 循环去遍历集合,而是使用 stream().forEach() 来执行操作。

  • 惰性求值:Stream 的中间操作是惰性求值的,只有在调用终端操作时才会执行。这使得流可以进行优化。

  • 无副作用Stream 操作一般是无状态和无副作用的,也就是说,它们不影响原始的数据源。

3. Stream 的基本操作类型

Stream 主要由两类操作组成:

  1. 中间操作(Intermediate Operations):返回一个新的 Stream。这类操作是惰性求值的。
  2. 终端操作(Terminal Operations):产生结果或者副作用,执行后会结束流。
中间操作:
  1. filter(Predicate<? super T> predicate)

    • 用于过滤数据,保留符合条件的元素。

    • 例子:

      java 复制代码
      List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
      List<String> result = names.stream()
                                 .filter(name -> name.startsWith("A"))
                                 .collect(Collectors.toList());
      // 输出: ["Alice"]
  2. map(Function<? super T, ? extends R> mapper)

    • 将每个元素映射为另一个类型,可以进行类型转换。

    • 例子:

      java 复制代码
      List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
      List<Integer> nameLengths = names.stream()
                                       .map(String::length)
                                       .collect(Collectors.toList());
      // 输出: [5, 3, 7]
  3. sorted(Comparator<? super T> comparator)

    • 对流中的元素进行排序,可以传入自定义的比较器。

    • 例子:

      java 复制代码
      List<String> names = Arrays.asList("Bob", "Alice", "Charlie");
      List<String> sortedNames = names.stream()
                                      .sorted()
                                      .collect(Collectors.toList());
      // 输出: ["Alice", "Bob", "Charlie"]
  4. distinct()

    • 去除流中的重复元素。

    • 例子:

      java 复制代码
      List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 4, 4);
      List<Integer> distinctNumbers = numbers.stream()
                                             .distinct()
                                             .collect(Collectors.toList());
      // 输出: [1, 2, 3, 4]
  5. limit(long maxSize)

    • 截取流中的前 maxSize 个元素。

    • 例子:

      java 复制代码
      List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
      List<Integer> limitedNumbers = numbers.stream()
                                            .limit(3)
                                            .collect(Collectors.toList());
      // 输出: [1, 2, 3]
  6. skip(long n)

    • 跳过前 n 个元素,保留后面的元素。

    • 例子:

      java 复制代码
      List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
      List<Integer> skippedNumbers = numbers.stream()
                                            .skip(2)
                                            .collect(Collectors.toList());
      // 输出: [3, 4, 5]
终端操作:
  1. forEach(Consumer<? super T> action)

    • 对流中的每个元素执行某种操作。注意,这是终端操作,一旦执行,流不再可用。

    • 例子:

      java 复制代码
      List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
      names.stream().forEach(System.out::println);
  2. collect(Collector<? super T, A, R> collector)

    • 用于将流中的元素收集到某种结果中,通常是 ListSetMap 等集合。

    • 例子:

      java 复制代码
      List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
      List<String> collectedNames = names.stream().collect(Collectors.toList());
  3. reduce(BinaryOperator<T> accumulator)

    • 对流中的元素进行累积操作,例如求和、求积等。

    • 例子:

      java 复制代码
      List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
      int sum = numbers.stream().reduce(0, Integer::sum);
      // 输出: 10
  4. count()

    • 返回流中元素的个数。

    • 例子:

      java 复制代码
      List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
      long count = names.stream().count();
      // 输出: 3
  5. findFirst()

    • 返回流中的第一个元素。

    • 例子:

      java 复制代码
      List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
      Optional<String> firstName = names.stream().findFirst();
      // 输出: Alice
  6. anyMatch(Predicate<? super T> predicate)

    • 流中是否有任意元素匹配给定的条件。

    • 例子:

      java 复制代码
      List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
      boolean hasAlice = names.stream().anyMatch(name -> name.equals("Alice"));
      // 输出: true
4. Stream API 的工作原理

Stream API 的工作过程分为三个主要步骤:

  1. 创建流:从集合或数组生成流对象。

    • 可以通过 Collection 接口中的 stream() 方法,或 Arrays.stream() 方法创建流。
  2. 中间操作:链式调用的中间操作不会立即执行,而是建立处理流水线,直到终端操作触发整个流程。

  3. 终端操作:一旦调用终端操作,流中的数据处理开始执行,产生结果,整个 Stream 不再可用。

5. 并行流(Parallel Streams)

Java 8 中还引入了 并行流 的概念,可以通过 parallelStream() 方法或 stream().parallel() 创建。并行流将任务分割并分配给多个线程,提高大数据量下的处理性能。

例子:

java 复制代码
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.parallelStream().reduce(0, Integer::sum);

并行流适合大规模数据集和计算密集型任务,但并非总是比顺序流快。应根据具体情况测试其性能。

6. Stream API 使用的最佳实践
  1. 尽量使用中间操作而不是修改外部变量:流操作应该是无副作用的,尽量不要在中间操作中修改外部变量。

  2. 避免不必要的并行流:并行流不总是能提高性能,特别是在小数据集上。

  3. 善用短路操作findFirst()anyMatch() 等操作会在找到结果时提前结束流的处理,适用于需要快速得到结果的场景。

7. Stream API 的优点
  1. 提高代码可读性:通过函数式编程的方式处理数据,简化了传统命令式编程中的冗余代码。
  2. 支持并行处理:并行流提供了处理大数据集的高效手段。
  3. 代码简洁:通过链式操作,使得代码更加紧凑、简洁。
8. 总结

Stream API 是 Java 8 引入的一项强大功能,它简化了集合的处理方式,支持声明式编程、无副作用操作,并具备强大的并行处理能力。掌握它能够帮助开发者写出更简洁、高效的代码。在实际使用中,需要根据具体场景选择合适的流操作

方式,提升应用程序的性能和可维护性。

记忆秘诀

  • 是数据流转的过程,不存储数据,只传递、处理数据。
  • 中间操作 总是惰性求值,等到 终端操作 执行时,才真正处理数据。
相关推荐
风象南5 分钟前
SpringBoot 控制器的动态注册与卸载
java·spring boot·后端
醇醛酸醚酮酯23 分钟前
Qt项目锻炼——TODO清单(二)
开发语言·数据库·qt
前端付豪27 分钟前
17、自动化才是正义:用 Python 接管你的日常琐事
后端·python
jioulongzi28 分钟前
记录一次莫名奇妙的跨域502(badgateway)错误
开发语言·python
我是一只代码狗31 分钟前
springboot中使用线程池
java·spring boot·后端
skywalk816344 分钟前
2025年的前后端一体化CMS框架优选方案
cms·web
hello早上好44 分钟前
JDK 代理原理
java·spring boot·spring
PanZonghui1 小时前
Centos项目部署之安装数据库MySQL8
linux·后端·mysql
PanZonghui1 小时前
Centos项目部署之Java安装与配置
java·linux
Victor3561 小时前
MySQL(119)如何加密存储敏感数据?
后端