Java Streams 使用 toMap 和 groupingBy 的方法及其异同

背景

今天在处理一个数据分组问题是,突然想到是使用 toMap 呢还是使用 groupBy 呢,因为这两个 API 都是比较常用的,一时语塞,后来想想还是比较好区分的,下面就介绍下这两个常用的 API。

Java 8 引入了 Stream API,它为处理集合数据提供了一个声明性的方法。Stream API 提供了一系列操作来对数据进行转换和聚合,其中 toMapgroupingBy 是两个常用的终端操作(terminal operation)。

1. toMap 操作

toMap 是 Stream 的一个收集器(collector),它将流中的元素转换为一个 Map。该方法常用于需要将流中的数据转换为键值对的场景。

用法示例:

java 复制代码
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class ToMapExample {
    public static void main(String[] args) {
        List<Student> students = Arrays.asList(
            new Student(1, "Alice"),
            new Student(2, "Bob"),
            new Student(3, "Charlie")
        );

        Map<Integer, String> studentMap = students.stream()
            .collect(Collectors.toMap(Student::getId, Student::getName));

        System.out.println(studentMap);
    }
}

@Data
class Student {
    private int id;
    private String name;

    public Student(int id, String name) {
        this.id = id;
        this.name = name;
    }
}

在这个示例中,我们将学生列表转换为一个以学生 ID 为键,学生名字为值的 Map

注意事项:

  • toMap 要求键是唯一的,如果流中存在重复键,会抛出 IllegalStateException

  • 可以传递第三个参数来解决键冲突:

java 复制代码
.collect(Collectors.toMap(Student::getId, Student::getName, (name1, name2) -> name1));

(name1, name2) -> name1) 表示键冲突时,保留前一个数据,类似的,可以保留后面的数据。

2. groupingBy 操作

groupingBy 是另一个收集器,它基于某个分类函数对流中的元素进行分组,返回一个 Map,键为分类依据,值为该分类下的元素列表。

用法示例:

java 复制代码
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class GroupingByExample {
    public static void main(String[] args) {
        List<Student> students = Arrays.asList(
            new Student(1, "Alice", "A"),
            new Student(2, "Bob", "B"),
            new Student(3, "Charlie", "A")
        );

        Map<String, List<Student>> studentGroups = students.stream()
            .collect(Collectors.groupingBy(Student::getGrade));

        System.out.println(studentGroups);
    }
}

@Data
class Student {
    private int id;
    private String name;
    private String grade;

    public Student(int id, String name, String grade) {
        this.id = id;
        this.name = name;
        this.grade = grade;
    }
}

在这个示例中,我们根据学生的年级对学生进行分组,生成一个以年级为键,学生列表为值的 Map

注意事项:

  • groupingBy 的键可以是任何类型,且键不需要唯一。
  • 可以通过 Collectors.mapping 等进一步进行嵌套收集。

异同

相同点:

  • 都是 Collectors 提供的静态方法。
  • 都是终端操作,用于将流中的数据收集到 Map 中。

不同点:

  • 目的不同toMap 用于将流中的数据转换为键值对,而 groupingBy 用于根据某个分类标准对数据进行分组。
  • 返回结果toMap 返回的是一个单层的 Map,键为唯一值;groupingBy 返回的是一个嵌套的 Map,键为分类标准,值为该分类下的元素列表。
  • 键的唯一性toMap 要求键唯一,若不唯一则需要处理冲突;groupingBy 不要求键唯一,因为每个键对应的是一个元素列表。

总结

返回的是一个嵌套的Map`,键为分类标准,值为该分类下的元素列表。

  • 键的唯一性toMap 要求键唯一,若不唯一则需要处理冲突;groupingBy 不要求键唯一,因为每个键对应的是一个元素列表。

总结

toMapgroupingBy 是 Java Stream API 中强大的收集器,它们在数据转换和分组处理中有着广泛的应用。toMap 适用于需要将流中的数据映射为键值对的情况,而 groupingBy 则适用于需要对数据进行分类和分组的情况。理解它们的使用方法和区别,有助于更高效地处理集合数据。

相关推荐
FQNmxDG4S8 小时前
Java多线程编程:Thread与Runnable的并发控制
java·开发语言
虹科网络安全9 小时前
艾体宝干货|数据复制详解:类型、原理与适用场景
java·开发语言·数据库
axng pmje9 小时前
Java语法进阶
java·开发语言·jvm
rKWP8gKv79 小时前
Java微服务性能监控:Prometheus与Grafana集成方案
java·微服务·prometheus
老前端的功夫10 小时前
【Java从入门到入土】28:Stream API:告别for循环的新时代
java·开发语言·python
qq_4352879210 小时前
第9章 夸父逐日与后羿射日:死循环与进程终止?十个太阳同时值班的并行冲突
java·开发语言·git·死循环·进程终止·并行冲突·夸父逐日
小江的记录本10 小时前
【Kafka核心】架构模型:Producer、Broker、Consumer、Consumer Group、Topic、Partition、Replica
java·数据库·分布式·后端·搜索引擎·架构·kafka
yaoxin52112310 小时前
397. Java 文件操作基础 - 创建常规文件与临时文件
java·开发语言·python
极客先躯12 小时前
高级java每日一道面试题-2025年11月24日-容器与虚拟化题[Dockerj]-runc 的作用是什么?
java·oci 的命令行工具·最小可用·无守护进程·完全标准·创建容器的核心流程·runc 核心职责思维导图
用户606487671889612 小时前
AI 抢不走的技能:用 Claude API 构建自动化工作流实战
java