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

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

记忆秘诀

  • 是数据流转的过程,不存储数据,只传递、处理数据。
  • 中间操作 总是惰性求值,等到 终端操作 执行时,才真正处理数据。
相关推荐
徐小黑ACG17 分钟前
GO语言 使用protobuf
开发语言·后端·golang·protobuf
0白露1 小时前
Apifox Helper 与 Swagger3 区别
开发语言
Tanecious.2 小时前
机器视觉--python基础语法
开发语言·python
叠叠乐2 小时前
rust Send Sync 以及对象安全和对象不安全
开发语言·安全·rust
战族狼魂3 小时前
CSGO 皮肤交易平台后端 (Spring Boot) 代码结构与示例
java·spring boot·后端
Tttian6224 小时前
Python办公自动化(3)对Excel的操作
开发语言·python·excel
xyliiiiiL4 小时前
ZGC初步了解
java·jvm·算法
杉之4 小时前
常见前端GET请求以及对应的Spring后端接收接口写法
java·前端·后端·spring·vue
hycccccch5 小时前
Canal+RabbitMQ实现MySQL数据增量同步
java·数据库·后端·rabbitmq
独好紫罗兰5 小时前
洛谷题单2-P5713 【深基3.例5】洛谷团队系统-python-流程图重构
开发语言·python·算法