当前位置:首页 > 文章列表 > 文章 > java教程 > Java多字段分组取最新记录技巧

Java多字段分组取最新记录技巧

2025-09-13 13:36:45 0浏览 收藏

哈喽!大家好,很高兴又见面了,我是golang学习网的一名作者,今天由我给大家带来一篇《Java多字段分组筛选最新记录方法》,本文主要会讲到等等知识点,希望大家一起学习进步,也欢迎大家关注、点赞、收藏、转发! 下面就一起来看看吧!

使用Java Stream API按多字段分组并筛选最新记录

本教程将演示如何利用Java Stream API高效处理对象列表,根据对象的多个字段进行分组,并从每个分组中选取出满足特定条件的唯一记录。我们将以员工数据为例,展示如何通过Collectors.toMap结合自定义合并逻辑,实现按姓氏和名字组合筛选出最新薪资记录的需求。

业务场景概述

在实际开发中,我们经常会遇到需要处理包含重复数据的列表,但这些重复数据在某些关键字段上是唯一的。例如,一个员工列表可能包含同一个员工(通过姓氏和名字确定)在不同时间点的薪资记录。我们的目标是,对于每个唯一的员工组合(例如,相同的姓氏和名字),只保留其最新的一条记录。

假设我们有以下Employee类定义:

import lombok.AllArgsConstructor;
import lombok.Data;

import java.time.LocalDate;

@Data
@AllArgsConstructor
public class Employee {
    private String firstName;
    private String lastName;
    private double salary;
    private LocalDate date; // 使用LocalDate表示日期
}

以及一个包含示例数据的List

import java.util.ArrayList;
import java.util.List;
import java.time.LocalDate;

public class EmployeeData {
    public static List<Employee> getSampleEmployees() {
        List<Employee> employees = new ArrayList<>();
        employees.add(new Employee("John", "Smith", 10, LocalDate.of(2022, 9, 1)));
        employees.add(new Employee("John", "Smith", 20, LocalDate.of(2022, 10, 1)));
        employees.add(new Employee("John", "Smith", 5, LocalDate.of(2022, 11, 1)));
        employees.add(new Employee("Kelly", "Jones", 12, LocalDate.of(2022, 3, 1)));
        employees.add(new Employee("Sara", "Kim", 21, LocalDate.of(2022, 3, 1)));
        employees.add(new Employee("Sara", "Kim", 7, LocalDate.of(2022, 7, 1)));
        return employees;
    }
}

我们的目标是得到以下结果:

  • "John", "Smith", 5, 2022-11-01 (最新日期)
  • "Kelly", "Jones", 12, 2022-03-01 (唯一记录)
  • "Sara", "Kim", 7, 2022-07-01 (最新日期)

使用 Java Stream API 解决问题

Java 8 引入的 Stream API 为处理集合数据提供了强大而简洁的工具。对于上述需求,我们可以利用Collectors.toMap的重载方法,该方法允许我们定义一个合并函数来处理键冲突。

核心思路

  1. 生成唯一的键: 对于每个Employee对象,我们需要一个唯一的键来表示一个特定的员工。在这个例子中,firstName和lastName的组合构成了这个键。
  2. 处理键冲突: 当两个或多个Employee对象生成相同的键时(即它们是同一个员工),我们需要一个策略来决定保留哪一个。在本例中,我们希望保留日期最新的那条记录。

示例代码

import java.util.Collection;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.time.LocalDate;

public class EmployeeFilterTutorial {

    public static void main(String[] args) {
        List<Employee> employees = EmployeeData.getSampleEmployees();

        Collection<Employee> filteredEmployees = employees.stream()
                .collect(Collectors.toMap(
                        // keyMapper: 定义如何从Employee对象中提取键
                        // 这里将firstName和lastName拼接作为键
                        employee -> employee.getFirstName() + employee.getLastName(),
                        // valueMapper: 定义如何从Employee对象中提取值
                        // Function.identity() 表示Employee对象本身作为值
                        Function.identity(),
                        // mergeFunction: 定义当两个键相同时如何合并值
                        // 比较两个Employee对象的日期,保留日期最新的那个
                        (existingEmployee, newEmployee) -> existingEmployee.getDate().isAfter(newEmployee.getDate()) ? existingEmployee : newEmployee
                ))
                .values(); // 获取Map中所有的值,即我们筛选后的Employee对象

        // 打印结果
        filteredEmployees.forEach(System.out::println);
    }
}

代码解析

  1. employees.stream(): 创建一个Employee对象的流。
  2. .collect(Collectors.toMap(...)): 这是核心操作。Collectors.toMap有多个重载方法,这里我们使用的是接受三个参数的版本:
    • keyMapper (employee -> employee.getFirstName() + employee.getLastName()): 这个函数负责从每个Employee对象中提取出用于作为Map键的部分。在这里,我们将firstName和lastName字符串拼接起来,形成一个唯一的员工标识符。
      • 注意事项: 字符串拼接作为键简单易行,但在某些情况下可能存在性能问题或潜在的哈希冲突风险(尽管对于人名组合通常足够)。更健壮的方法是定义一个包含firstName和lastName的自定义类作为键,并正确实现其equals()和hashCode()方法。
    • valueMapper (Function.identity()): 这个函数负责从Employee对象中提取出用于作为Map值的部分。Function.identity()是一个方便的静态方法,它表示将输入对象本身作为输出,即Employee对象本身将作为Map的值。
    • mergeFunction ((existingEmployee, newEmployee) -> existingEmployee.getDate().isAfter(newEmployee.getDate()) ? existingEmployee : newEmployee): 这是解决键冲突的关键。当keyMapper生成相同的键时,toMap会调用这个函数来决定保留哪个值。
      • existingEmployee:是Map中已经存在的值(即之前处理过的相同键的Employee对象)。
      • newEmployee:是当前正在处理的、与existingEmployee具有相同键的Employee对象。
      • 我们的逻辑是比较它们的date字段。如果existingEmployee的日期在newEmployee之后,则保留existingEmployee;否则,保留newEmployee。这确保了Map中最终存储的是具有最新日期的Employee记录。
  3. .values(): Collectors.toMap操作返回一个Map。我们只需要最终筛选出来的Employee对象,因此调用.values()方法获取Map中所有值的Collection。

运行结果

执行上述代码,将得到以下输出:

Employee(firstName=John, lastName=Smith, salary=5.0, date=2022-11-01)
Employee(firstName=Sara, lastName=Kim, salary=7.0, date=2022-07-01)
Employee(firstName=Kelly, lastName=Jones, salary=12.0, date=2022-03-01)

这正是我们期望的结果,对于每个唯一的员工组合,都成功筛选出了日期最新的那条记录。

总结与注意事项

  • Collectors.toMap的强大之处: 通过提供一个自定义的合并函数,Collectors.toMap可以非常灵活地处理键冲突,这使其成为根据特定条件从分组数据中选择唯一记录的理想工具。
  • 灵活性: mergeFunction可以根据不同的业务需求进行调整。例如,如果你想保留薪资最高的记录,可以将比较逻辑改为existingEmployee.getSalary() > newEmployee.getSalary() ? existingEmployee : newEmployee。
  • 键的生成: 确保键的生成方式能够准确地识别你想要分组的唯一实体。对于多字段组合键,字符串拼接是一种简单的方法,但对于复杂对象或性能敏感的场景,自定义键对象并正确实现equals()和hashCode()会是更好的选择。
  • 替代方案: 另一种实现方式是先使用Collectors.groupingBy按多字段分组,得到Map>,然后再对每个List进行排序和选择。但对于本例这种直接选择一个“最佳”记录的场景,Collectors.toMap通常更简洁高效。

通过本教程,您应该已经掌握了如何使用Java Stream API结合Collectors.toMap,根据对象的多个字段进行分组,并从每个分组中筛选出符合特定条件的唯一记录。这种模式在数据处理和聚合中非常常见且实用。

文中关于的知识介绍,希望对你的学习有所帮助!若是受益匪浅,那就动动鼠标收藏这篇《Java多字段分组取最新记录技巧》文章吧,也可关注golang学习网公众号了解相关技术文章。

Jira隐藏滚动条技巧,优化管理界面体验Jira隐藏滚动条技巧,优化管理界面体验
上一篇
Jira隐藏滚动条技巧,优化管理界面体验
RealPlayer查看版本方法详解
下一篇
RealPlayer查看版本方法详解
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    514次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    499次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    484次学习
查看更多
AI推荐
  • SEO  AI Mermaid 流程图:自然语言生成,文本驱动可视化创作
    AI Mermaid流程图
    SEO AI Mermaid 流程图工具:基于 Mermaid 语法,AI 辅助,自然语言生成流程图,提升可视化创作效率,适用于开发者、产品经理、教育工作者。
    391次使用
  • 搜获客笔记生成器:小红书医美爆款内容AI创作神器
    搜获客【笔记生成器】
    搜获客笔记生成器,国内首个聚焦小红书医美垂类的AI文案工具。1500万爆款文案库,行业专属算法,助您高效创作合规、引流的医美笔记,提升运营效率,引爆小红书流量!
    373次使用
  • iTerms:一站式法律AI工作台,智能合同审查起草与法律问答专家
    iTerms
    iTerms是一款专业的一站式法律AI工作台,提供AI合同审查、AI合同起草及AI法律问答服务。通过智能问答、深度思考与联网检索,助您高效检索法律法规与司法判例,告别传统模板,实现合同一键起草与在线编辑,大幅提升法律事务处理效率。
    403次使用
  • TokenPony:AI大模型API聚合平台,一站式接入,高效稳定高性价比
    TokenPony
    TokenPony是讯盟科技旗下的AI大模型聚合API平台。通过统一接口接入DeepSeek、Kimi、Qwen等主流模型,支持1024K超长上下文,实现零配置、免部署、极速响应与高性价比的AI应用开发,助力专业用户轻松构建智能服务。
    386次使用
  • 迅捷AIPPT:AI智能PPT生成器,高效制作专业演示文稿
    迅捷AIPPT
    迅捷AIPPT是一款高效AI智能PPT生成软件,一键智能生成精美演示文稿。内置海量专业模板、多样风格,支持自定义大纲,助您轻松制作高质量PPT,大幅节省时间。
    380次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码