如何在 Java 中使用流 API 处理集合

JavaJavaBeginner
立即练习

💡 本教程由 AI 辅助翻译自英文原版。如需查看原文,您可以 切换至英文原版

简介

Java 流 API 提供了一种强大且灵活的方式来处理数据集合。在本教程中,我们将探讨如何使用流 API 对集合执行常见操作和转换,使你能够编写更简洁、易读且高效的 Java 代码。


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL java(("Java")) -.-> java/DataStructuresGroup(["Data Structures"]) java(("Java")) -.-> java/ProgrammingTechniquesGroup(["Programming Techniques"]) java(("Java")) -.-> java/FileandIOManagementGroup(["File and I/O Management"]) java(("Java")) -.-> java/ConcurrentandNetworkProgrammingGroup(["Concurrent and Network Programming"]) java/DataStructuresGroup -.-> java/collections_methods("Collections Methods") java/ProgrammingTechniquesGroup -.-> java/method_overloading("Method Overloading") java/FileandIOManagementGroup -.-> java/io("IO") java/FileandIOManagementGroup -.-> java/stream("Stream") java/ConcurrentandNetworkProgrammingGroup -.-> java/working("Working") subgraph Lab Skills java/collections_methods -.-> lab-415588{{"如何在 Java 中使用流 API 处理集合"}} java/method_overloading -.-> lab-415588{{"如何在 Java 中使用流 API 处理集合"}} java/io -.-> lab-415588{{"如何在 Java 中使用流 API 处理集合"}} java/stream -.-> lab-415588{{"如何在 Java 中使用流 API 处理集合"}} java/working -.-> lab-415588{{"如何在 Java 中使用流 API 处理集合"}} end

理解 Java 流 API

Java 流 API 是 Java 8 中引入的一项强大功能,它允许你以更声明式和函数式的风格处理数据集合。流提供了一种对集合执行各种操作的方式,例如过滤、映射、排序和归约,而无需显式迭代或可变状态。

什么是 Java 流?

Java 流是一系列元素,支持以类似管道的方式执行各种操作。流本身不是数据结构,而是在现有数据结构(如集合、数组或 I/O 资源)之上进行操作。

流为处理集合提供了高级抽象,使你能够专注于做什么(期望的结果)而不是如何做(实现细节)。

Java 流的关键特性

  1. 惰性求值:流是惰性的,这意味着它们仅在请求操作时才执行必要的计算。这可以提高性能并减少内存使用。
  2. 函数式:流促进函数式编程风格,其中操作被定义为将输入数据转换为期望输出的纯函数。
  3. 并行性:流可以轻松并行化,从而有效利用多核处理器并提高对大型数据集的处理性能。
  4. 流畅 API:流提供了流畅的 API,多个操作可以链接在一起以创建复杂的数据处理管道。

流管道剖析

典型的流管道由三个主要部分组成:

  1. :初始数据源,如集合、数组或 I/O 资源。
  2. 中间操作:一个或多个转换数据的操作,如过滤、映射或排序。
  3. 终端操作:产生期望结果的最终操作,如归约、收集或迭代。

以下是一个对流中的数字进行过滤、映射和归约的流管道示例:

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

int sum = numbers.stream()
                 .filter(n -> n % 2 == 0)
                 .map(n -> n * 2)
                 .reduce(0, Integer::sum);

System.out.println("Sum of even numbers doubled: " + sum); // 输出:110

在下一节中,我们将更深入地探讨可用于处理数据的常见流操作和转换。

常见的流操作和转换

Java 流提供了各种各样的操作和转换,可用于处理你的数据。让我们来探讨一些最常见的操作:

过滤

filter() 操作允许你根据给定的谓词从流中选择元素。例如,要从列表中过滤出偶数:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> evenNumbers = numbers.stream()
                                  .filter(n -> n % 2 == 0)
                                  .collect(Collectors.toList());

System.out.println(evenNumbers); // 输出:[2, 4, 6, 8, 10]

映射

map() 操作允许你使用提供的函数转换流中的每个元素。例如,要将列表中每个数字的值翻倍:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> doubledNumbers = numbers.stream()
                                     .map(n -> n * 2)
                                     .collect(Collectors.toList());

System.out.println(doubledNumbers); // 输出:[2, 4, 6, 8, 10]

排序

sorted() 操作允许你根据元素的自然顺序或自定义比较器对流中的元素进行排序。例如,要按字母逆序对字符串列表进行排序:

List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
List<String> sortedNames = names.stream()
                               .sorted(Comparator.reverseOrder())
                               .collect(Collectors.toList());

System.out.println(sortedNames); // 输出:[David, Charlie, Bob, Alice]

归约

reduce() 操作允许你将流中的元素组合成单个值。例如,要计算数字列表的总和:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()
                 .reduce(0, Integer::sum);

System.out.println(sum); // 输出:15

收集

collect() 操作允许你将流管道的结果收集到新的数据结构中,如 ListSetMap。例如,要收集字符串列表的唯一长度:

List<String> words = Arrays.asList("apple", "banana", "cherry", "date", "elderberry");
Set<Integer> uniqueLengths = words.stream()
                                 .map(String::length)
                                 .collect(Collectors.toSet());

System.out.println(uniqueLengths); // 输出:[5, 6, 4]

这些只是 Java 流 API 中众多操作和转换的几个示例。在下一节中,我们将探讨一些使用流的实际示例和用例。

流的实际示例和用例

既然我们已经介绍了 Java 流 API 的基础知识,那就来探讨一些实际的示例和用例。

过滤和转换数据

假设你有一个 Person 对象列表,你想找出所有成年人(年龄 18 岁及以上),并创建一个新的列表,其中包含他们名字的大写形式:

class Person {
    private String name;
    private int age;

    // 构造函数、getter 和 setter
}

List<Person> people = Arrays.asList(
    new Person("Alice", 25),
    new Person("Bob", 17),
    new Person("Charlie", 32),
    new Person("David", 14)
);

List<String> adultNames = people.stream()
                               .filter(p -> p.getAge() >= 18)
                               .map(Person::getName)
                               .map(String::toUpperCase)
                               .collect(Collectors.toList());

System.out.println(adultNames); // 输出:[ALICE, CHARLIE]

分组和汇总数据

假设你有一个产品列表,你想按类别对它们进行分组,并计算每个类别的总价:

class Product {
    private String name;
    private String category;
    private double price;

    // 构造函数、getter 和 setter
}

List<Product> products = Arrays.asList(
    new Product("Apple", "Fruit", 0.5),
    new Product("Banana", "Fruit", 0.25),
    new Product("Carrot", "Vegetable", 0.3),
    new Product("Broccoli", "Vegetable", 0.4)
);

Map<String, Double> totalPriceByCategory = products.stream()
                                                 .collect(Collectors.groupingBy(
                                                      Product::getCategory,
                                                      Collectors.summingDouble(Product::getPrice)
                                                  ));

System.out.println(totalPriceByCategory); // 输出:{Fruit=0.75, Vegetable=0.7}

并行处理

流可以很容易地并行化,以利用多核处理器并提高对大型数据集的处理性能。以下是一个如何使用并行流来计算前 100 万个整数之和的示例:

long sum = LongStream.rangeClosed(1, 1_000_000)
                    .parallel()
                    .sum();

System.out.println(sum); // 输出:500000500000

通过使用 parallel() 方法,流操作会分布在多个线程上,从而使计算能够更高效地执行。

这些只是 Java 流 API 众多实际用例中的几个示例。流的强大之处在于它们能够以简洁且易读的方式表达复杂的数据处理管道,同时还提供了优化性能和可扩展性的灵活性。

总结

在本教程结束时,你将对 Java 流 API 有扎实的理解,以及如何应用它来有效地处理集合。你将学习常见的流操作、实际示例和实际应用场景,从而提升你的 Java 编程技能,并编写更具表现力、函数式和高性能的代码。