如何在 Java 中收集流结果

JavaJavaBeginner
立即练习

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

简介

Java 流 API 提供了强大的数据处理能力,使开发者能够使用函数式编程技术高效地操作和收集数据。本教程将探索收集流结果的各种方法,展示如何用简洁且富有表现力的代码来转换和聚合数据。


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL java(("Java")) -.-> java/DataStructuresGroup(["Data Structures"]) java(("Java")) -.-> java/ProgrammingTechniquesGroup(["Programming Techniques"]) java(("Java")) -.-> java/ObjectOrientedandAdvancedConceptsGroup(["Object-Oriented and Advanced Concepts"]) java(("Java")) -.-> java/FileandIOManagementGroup(["File and I/O Management"]) java/DataStructuresGroup -.-> java/collections_methods("Collections Methods") java/ProgrammingTechniquesGroup -.-> java/method_overloading("Method Overloading") java/ProgrammingTechniquesGroup -.-> java/lambda("Lambda") java/ObjectOrientedandAdvancedConceptsGroup -.-> java/arraylist("ArrayList") java/ObjectOrientedandAdvancedConceptsGroup -.-> java/generics("Generics") java/FileandIOManagementGroup -.-> java/stream("Stream") subgraph Lab Skills java/collections_methods -.-> lab-464457{{"如何在 Java 中收集流结果"}} java/method_overloading -.-> lab-464457{{"如何在 Java 中收集流结果"}} java/lambda -.-> lab-464457{{"如何在 Java 中收集流结果"}} java/arraylist -.-> lab-464457{{"如何在 Java 中收集流结果"}} java/generics -.-> lab-464457{{"如何在 Java 中收集流结果"}} java/stream -.-> lab-464457{{"如何在 Java 中收集流结果"}} end

流基础

什么是流?

在 Java 中,流是支持顺序和并行聚合操作的元素序列。流在 Java 8 中引入,为处理对象集合提供了一种声明式方法。

流的关键特性

特性 描述
函数式 支持函数式风格的操作
延迟求值 操作仅在需要时才计算
不可变 原始数据源保持不变
可消费 只能遍历一次

流的创建方法

// 从集合创建流
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
Stream<String> nameStream = names.stream();

// Stream.of() 方法
Stream<Integer> numberStream = Stream.of(1, 2, 3, 4, 5);

// 流生成
Stream<String> infiniteStream = Stream.generate(() -> "Hello");

流管道架构

graph LR A[源] --> B[中间操作] B --> C[终端操作]

流的类型

  1. 顺序流
    • 逐个处理元素
    • 默认流类型
    • 使用 stream() 方法
  2. 并行流
    • 并发处理元素
    • 使用 parallelStream() 方法
    • 对大型数据集可提高性能

基本流操作

中间操作

  • filter():根据谓词选择元素
  • map():转换元素
  • sorted():对流元素进行排序

终端操作

  • collect():收集流结果
  • forEach():对每个元素执行操作
  • reduce():将流缩减为单个值

示例:流处理

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()
              .filter(n -> n % 2 == 0)
              .mapToInt(n -> n * 2)
              .sum();
// 结果:12 (2 * 2 + 4 * 2)

何时使用流

  • 大型数据转换
  • 复杂数据过滤
  • 函数式编程场景
  • 并行处理需求

性能考虑

  • 小集合有开销
  • 最适合中大型数据集
  • 并行流有初始化成本

通过理解这些流基础,开发者可以在 Java 中利用强大、简洁的数据处理技术。LabEx 建议实践这些概念以有效掌握流操作。

收集操作

收集器简介

收集器是终端操作,用于将流元素转换为集合或聚合值。Collectors 类提供了许多预定义的方法来收集流结果。

基本收集器类型

收集器方法 描述 返回类型
toList() 将元素收集到列表中 List
toSet() 将唯一元素收集到集合中 Set
toCollection() 收集到特定的集合中 Collection
joining() 连接流元素 String
counting() 对流元素进行计数 Long

收集到集合

// 收集到列表
List<String> nameList = names.stream()
  .collect(Collectors.toList());

// 收集到集合
Set<String> uniqueNames = names.stream()
  .collect(Collectors.toSet());

// 收集到特定集合
LinkedList<String> linkedNames = names.stream()
  .collect(Collectors.toCollection(LinkedList::new));

分组和分区

graph TD A[流分组] --> B[groupingBy] A --> C[partitioningBy] B --> D[多个组] C --> E[布尔分区]

元素分组

// 按长度分组
Map<Integer, List<String>> groupedByLength = names.stream()
  .collect(Collectors.groupingBy(String::length));

// 使用下游收集器分组
Map<Integer, Long> countByLength = names.stream()
  .collect(Collectors.groupingBy(
        String::length,
        Collectors.counting()
    ));

元素分区

// 分成两组
Map<Boolean, List<String>> partitioned = names.stream()
  .collect(Collectors.partitioningBy(
        name -> name.length() > 4
    ));

高级收集操作

归约和汇总

// 整数之和
int total = numbers.stream()
  .collect(Collectors.summingInt(Integer::intValue));

// 汇总统计信息
IntSummaryStatistics stats = numbers.stream()
  .collect(Collectors.summarizingInt(Integer::intValue));

连接字符串

// 简单连接
String result = names.stream()
  .collect(Collectors.joining());

// 用分隔符连接
String commaSeparated = names.stream()
  .collect(Collectors.joining(", "));

自定义收集器

// 自定义收集器
List<String> customCollected = names.stream()
  .collect(
        ArrayList::new,
        ArrayList::add,
        ArrayList::addAll
    );

性能考虑

  • 收集器消耗内存
  • 根据用例选择合适的收集器
  • 避免不必要的中间集合

最佳实践

  1. 尽可能使用内置收集器
  2. 考虑内存消耗
  3. 根据具体需求选择合适的收集器

LabEx 建议实践这些收集操作,以熟练掌握流处理技术。

实际示例

现实世界中的流处理场景

1. 数据过滤与转换

class Employee {
    private String name;
    private double salary;
    private Department department;

    // 构造函数、getter 和 setter
}

// 筛选高绩效员工
List<Employee> topPerformers = employees.stream()
  .filter(e -> e.getSalary() > 50000)
  .filter(e -> e.getDepartment() == Department.ENGINEERING)
  .collect(Collectors.toList());

2. 复杂数据聚合

graph TD A[原始数据] --> B[过滤] B --> C[分组] C --> D[聚合] D --> E[结果]

部门薪资分析

// 按部门计算平均薪资
Map<Department, Double> avgSalaryByDept = employees.stream()
  .collect(Collectors.groupingBy(
        Employee::getDepartment,
        Collectors.averagingDouble(Employee::getSalary)
    ));

3. 数据转换技术

映射与提取

// 提取唯一姓名
Set<String> uniqueNames = employees.stream()
  .map(Employee::getName)
  .collect(Collectors.toSet());

// 创建姓名 - 薪资映射
Map<String, Double> nameSalaryMap = employees.stream()
  .collect(Collectors.toMap(
        Employee::getName,
        Employee::getSalary
    ));

4. 并行处理

处理类型 特点 使用场景
顺序处理 单线程 小数据集
并行处理 多线程 大数据集
// 对大型员工列表进行并行处理
double totalSalary = employees.parallelStream()
  .mapToDouble(Employee::getSalary)
  .sum();

5. 高级过滤技术

// 复杂条件过滤
List<Employee> seniorEngineers = employees.stream()
  .filter(e -> e.getDepartment() == Department.ENGINEERING)
  .filter(e -> e.getExperience() > 5)
  .filter(e -> e.getSalary() > 75000)
  .collect(Collectors.toList());

6. 自定义收集器

// 用于找出前 N 名员工的自定义收集器
Collector<Employee,?, List<Employee>> topNCollector =
    Collectors.collectingAndThen(
        Collectors.toList(),
        list -> list.stream()
          .sorted(Comparator.comparing(Employee::getSalary).reversed())
          .limit(5)
          .collect(Collectors.toList())
    );

List<Employee> topFiveEmployees = employees.stream()
  .collect(topNCollector);

性能与最佳实践

  1. 使用适当的流操作
  2. 避免不必要的中间操作
  3. 考虑数据大小和复杂度
  4. 对流处理进行性能分析和基准测试

流中的错误处理

// 带有异常处理的安全处理
List<String> processedData = rawData.stream()
  .map(this::safeProcessing)
  .filter(Optional::isPresent)
  .map(Optional::get)
  .collect(Collectors.toList());

结论

流提供了强大且灵活的数据处理能力。LabEx 建议持续练习以有效掌握这些技术。

总结

理解 Java 中的流结果收集对于编写简洁、高效且具有函数式风格的代码至关重要。通过掌握不同的收集技术和收集器,开发者能够简化数据处理、提高代码可读性,并充分利用 Java 流 API 的全部潜力来进行复杂的数据转换。