当前位置: 首页 > article >正文

Java常用类-比较器

目录

  • 一、为什么需要比较器?
  • 二、核心差异速记表
  • 三、Comparable:对象自带的 “默认规则”
    • 1. 核心作用
    • 2. 源码定义
    • 3. 实战:给Student类加默认规则
    • 4. 源码验证(以Integer为例)
  • 四、Comparator:临时的 “外部规则”
    • 1. 核心作用
    • 2. 源码定义
    • 3. 实战:给 Student 加临时规则
      • 方式 1:匿名内部类(传统写法)
      • 方式 2:Lambda 表达式(Java 8+ 简化写法)
    • 4. 源码验证(以 Arrays.sort 为例)
      • 4.1 Arrays.sort()
        • 4.1.1 无Comparator时
        • 4.1.2 有 Comparator 时
      • 4.2 TreeMap中的比较器优先级
  • 五、Comparator 的进阶
    • 1. 多条件排序(先按 A,再按 B)
    • 2. 空值处理(允许 null)
    • 3. 反转顺序
  • 六、最佳实践总结
    • 1.​​比较器三原则​​(违反会导致排序异常):
    • 2.​​整数比较的陷阱​​
    • 3.选择策略​​:
    • 4.JDK8+高效写法​​:
  • 七、记忆口诀(一句话记住核心)
    • 1.Comparable:
    • 2.Comparator:
    • 3.优先级:
  • 八、常见面试题
    • 1.Comparable 和 Comparator 的区别?
    • 2.如果一个类没有实现 Comparable,能否排序?
    • 3.compareTo 和 compare 方法的返回值有什么要求?
    • 4.如何对自定义对象排序?
    • 5.Comparator 有哪些常用方法?
    • 6.如何实现逆序排序?​​
    • 7.​​比较器在HashMap中的作用?​​

一、为什么需要比较器?

场景:

  • 对 Integer、String 等内置类型排序时,JDK 知道怎么比大小(如 1 < 2,“a” < “b”)。
  • 但对自定义对象(如 User、Product),JDK 不知道按什么规则排序(按年龄?按价格?)。
    比较器就是用来定义 “对象比较规则” 的工具。

二、核心差异速记表

Comparable(自然排序)Comparator(定制排序)
​​包位置​​java.langjava.util
​​接口方法​​compareTo(T o)compare(T o1, T o2)
​​使用场景​​类本身的默认排序规则临时定义多种排序策略
规则位置写在类内部(实现接口)写在类外部(单独定义)
​​修改代码​​需要修改类源码不修改原有类
​​排序方式​​单一自然排序支持多种排序规则
​​典型应用​​String、Integer等包装类的排序第三方库排序,多条件排序

三、Comparable:对象自带的 “默认规则”

1. 核心作用

让对象 “自带” 一个比较规则,就像人天生知道 “年龄大的更年长” 一样。只要对象实现了Comparable,就能直接用Arrays.sort() 或Collections.sort() 排序。

2. 源码定义

// JDK 源码(java.lang.Comparable)
public interface Comparable<T> {int compareTo(T o); // 定义比较规则的方法
}
  • 返回值含义(重点!):
    • 如果 this < o → 返回负数(比如 -1)。
    • 如果 this == o → 返回 0。
    • 如果 this > o → 返回正数(比如 1)。

3. 实战:给Student类加默认规则

假设Student默认按分数升序排序:

public class Student implements Comparable<Student> {private String name;private int score;// 重写 compareTo:定义“分数升序”规则@Overridepublic int compareTo(Student other) {return this.score - other.score; // 分数小的排前面}// 构造方法、getter 省略...
}

使用示例:

Student[] students = {new Student("张三", 85),new Student("李四", 75),new Student("王五", 90)
};Arrays.sort(students); // 直接排序!因为 Student 实现了 Comparable
// 排序后顺序:李四(75)、张三(85)、王五(90)

4. 源码验证(以Integer为例)

Integer能直接排序,因为它实现了Comparable:

// JDK 源码(java.lang.Integer)
public final class Integer implements Comparable<Integer> {public int compareTo(Integer another) {return this.value - another.value; // 按数值大小比较}
}

四、Comparator:临时的 “外部规则”

1. 核心作用

当对象没有实现Comparable,或者需要临时改变排序规则(比如Student平时按分数排,但今天要按姓名排),就用Comparator。

2. 源码定义

// JDK 源码(java.util.Comparator)
@FunctionalInterface // 函数式接口(可 Lambda)
public interface Comparator<T> {int compare(T o1, T o2); // 定义比较规则的方法
}
  • 返回值含义和 Comparable 一致:
    • o1 < o2 → 负数;o1 == o2 → 0;o1 > o2 → 正数。**

3. 实战:给 Student 加临时规则

需求:Student平时按分数排(Comparable),但今天需要按姓名长度降序排。

方式 1:匿名内部类(传统写法)

// 定义一个“姓名长度降序”的比较器
Comparator<Student> nameLengthComparator = new Comparator<Student>() {@Overridepublic int compare(Student s1, Student s2) {// 姓名长度大的排前面(降序)return s2.getName().length() - s1.getName().length();}
};// 使用这个比较器排序
Arrays.sort(students, nameLengthComparator); 
// 排序后顺序:张三(2字)、李四(2字)、王五(2字)→ 若长度相同,保持原顺序

方式 2:Lambda 表达式(Java 8+ 简化写法)

// 用 Lambda 简化 Comparator 定义
Comparator<Student> nameLengthComparator = (s1, s2) -> s2.getName().length() - s1.getName().length();Arrays.sort(students, nameLengthComparator); // 效果同上

4. 源码验证(以 Arrays.sort 为例)

4.1 Arrays.sort()

// 在TimSort(Java排序算法实现)中的关键代码
if (c.compare(a[runHi++], a[lo]) < 0) { // 使用比较器判断顺序// 执行元素交换等操作
}

Arrays.sort 有两种重载,分别对应 Comparable 和 Comparator:

4.1.1 无Comparator时
public static <T extends Comparable<? super T>> void sort(T[] a) {// 直接调用对象的compareTo方法TimSort.sort(a, 0, a.length, null, 0, 0);
}

约束:数组元素必须实现Comparable,否则报ClassCastException。

4.1.2 有 Comparator 时
public static <T> void sort(T[] a, Comparator<? super T> c) {// 使用传入的ComparatorTimSort.sort(a, 0, a.length, c, 0, 0);
}

灵活性:无需元素实现Comparable,临时传入规则即可。

4.2 TreeMap中的比较器优先级

public TreeMap(Comparator<? super K> comparator) {this.comparator = comparator; // 比较器优先于自然排序
}final int compare(Object k1, Object k2) {return comparator==null ? ((Comparable)k1).compareTo(k2): comparator.compare(k1, k2);
}

五、Comparator 的进阶

1. 多条件排序(先按 A,再按 B)

需求:学生先按分数降序,分数相同则按姓名升序。

// 组合比较器:先按分数降序,分数相同按年龄升序
Comparator<Student> complexComparator = Comparator.comparingInt(Student::getScore).reversed() // 分数降序(先主条件).thenComparingInt(Student::getAge);// 分数相同,按姓名升序(次条件)Arrays.sort(students, multiComparator);// 相当于:
(s1, s2) -> {int scoreCompare = Integer.compare(s2.getScore(), s1.getScore());return (scoreCompare != 0) ? scoreCompare : Integer.compare(s1.getAge(), s2.getAge());
};

2. 空值处理(允许 null)

// null 排在最前面(nullsFirst)
Comparator<Student> nullsFirstComparator = Comparator.nullsFirst(Comparator.comparing(Student::getScore));// null 排在最后面(nullsLast)
Comparator<Student> nullsLastComparator = Comparator.nullsLast(Comparator.comparing(Student::getScore));

3. 反转顺序

Comparator<Student> scoreAsc = Comparator.comparingInt(Student::getScore); // 分数升序
Comparator<Student> scoreDesc = scoreAsc.reversed(); // 反转成降序

六、最佳实践总结

1.​​比较器三原则​​(违反会导致排序异常):

自反性:compare(a, a) == 0
对称性:compare(a, b) == -compare(b, a)
传递性:若compare(a, b) > 0且compare(b, c) > 0,则compare(a, c) > 0

2.​​整数比较的陷阱​​

// 错误写法(可能溢出):
return o1.id - o2.id;// 正确写法:
return Integer.compare(o1.id, o2.id);

3.选择策略​​:

  • 类有自然顺序 → 实现Comparable
  • 需要多种排序方式 → 使用Comparator
  • 第三方类排序 → 必须用Comparator

4.JDK8+高效写法​​:

// 多字段排序
users.sort(Comparator.comparing(User::getLastName).thenComparing(User::getFirstName));// 按字符串长度排序
Comparator.comparing(String::length);

七、记忆口诀(一句话记住核心)

1.Comparable:

  • 类内定义 “默认规则”,
  • 所有排序都用它(如学生默认按分数);
  • 例子:User implements Comparable,重写 compareTo。

2.Comparator:

  • 类外定义 “临时规则”,
  • 哪里需要哪里传(如学生临时按姓名);
  • 例子:Collections.sort(users, (u1, u2) -> …)。

3.优先级:

当Comparable和Comparator同时存在时,Comparator优先(覆盖默认规则)。

八、常见面试题

1.Comparable 和 Comparator 的区别?

答:Comparable 是类内部实现的接口(compareTo),定义对象的默认排序规则;Comparator 是外部定义的接口(compare),用于临时修改排序规则。

2.如果一个类没有实现 Comparable,能否排序?

答:可以!通过 Comparator 传入临时规则(如 Arrays.sort(数组, 自定义Comparator))。

3.compareTo 和 compare 方法的返回值有什么要求?

答:必须满足:负数(前者小)、0(相等)、正数(前者大)。如果返回值逻辑错误,排序会乱。

4.如何对自定义对象排序?

答:两种方式:
① 让类实现Comparable接口,重写compareTo。
② 不修改类,创建Comparator并传给排序方法。

5.Comparator 有哪些常用方法?

答:reversed()(反转)、thenComparing()(多条件排序)、nullsFirst()/nullsLast()(空值处理)。

6.如何实现逆序排序?​​

// 方法1:反转比较结果
Comparator<Student> reversed = (s1, s2) -> s2.compareTo(s1);// 方法2:使用内置方法
Comparator.comparing(Student::getScore).reversed();

7.​​比较器在HashMap中的作用?​​

HashMap不依赖比较器,但TreeMap的排序依赖比较器

相关文章:

Java常用类-比较器

目录 一、为什么需要比较器&#xff1f;二、核心差异速记表三、Comparable&#xff1a;对象自带的 “默认规则”1. 核心作用2. 源码定义3. 实战&#xff1a;给Student类加默认规则4. 源码验证&#xff08;以Integer为例&#xff09; 四、Comparator&#xff1a;临时的 “外部规…...

Python----机器学习(模型评估:准确率、损失函数值、精确度、召回率、F1分数、混淆矩阵、ROC曲线和AUC值、Top-k精度)

一、模型评估 1. 准确率&#xff08;Accuracy&#xff09;&#xff1a;这是最基本的评估指标之一&#xff0c;表示模型在测试集上正确 分类样本的比例。对于分类任务而言&#xff0c;准确率是衡量模型性能的直观标准。 2. 损失函数值&#xff08;Loss&#xff09;&#xff1…...

Linux工作台文件操作命令全流程解析(高级篇之vim和nano精讲)

全文目录 1 简单易用的 Nano (入门之选)1.1 适用场景1.2 安装命令1.3 基础操作1.4 优点 2 功能强大的 Vim2.1 适用场景2.2 安装命令2.3 模式说明‌2.4 常用命令2.4.1 普通模式2.4.2 编辑模式2.4.3 可视模式2.4.4 命令行模式 3 参考文献 写在前面 作为运维或者研发&#xff0c;日…...

大数据产品销售数据分析:基于Python机器学习产品销售数据爬虫可视化分析预测系统设计与实现

文章目录 大数据产品销售数据分析&#xff1a;基于Python机器学习产品销售数据爬虫可视化分析预测系统设计与实现一、项目概述二、项目说明三、研究意义四、系统总体架构设计总体框架技术架构数据可视化模块设计图后台管理模块设计数据库设计 五、开发技术介绍Flask框架Python爬…...

VS2022 Qt配置Qxlsx

目录 1、下载QXlsx&#xff0c;并解压文件夹 ​编辑2、打开VS2022配置QXlsx 3、VS配置Qxslx库 方法一&#xff1a;常规方法 方法二&#xff1a;直接使用源码 方法三&#xff1a;将QXlsx添加到Qt安装目录&#xff08;暂时尝试未成功&#xff09; 1、下载QXlsx&#xff0c;…...

OSPF案例

拓扑图&#xff1a; 要求&#xff1a; 1&#xff0c;R5为ISP&#xff0c;其上只能配置IP地址&#xff1b;R4作为企业边界路由器&#xff0c; 出口公网地址需要通过PPP协议获取&#xff0c;并进行chap认证 2&#xff0c;整个OSPF环境IP基于172.16.0.0/16划分&#xff1b;…...

1.1.2 简化迭代器 yield return的使用

yield return 是一个用于简化迭代器(Iterator)实现的关键字组合。它的核心作用是让开发者能够以更简洁的方式定义一个按需生成序列的方法(生成器方法),而无需显式实现 IEnumerable 或 IEnumerator 接口。yield return 方法会在每次迭代时按需生成下一个值,而不是一次性生…...

《用MATLAB玩转游戏开发》贪吃蛇的百变玩法:从命令行到AI对战

《用MATLAB玩转游戏开发&#xff1a;从零开始打造你的数字乐园》基础篇&#xff08;2D图形交互&#xff09;-&#x1f40d; 贪吃蛇的百变玩法&#xff1a;从命令行到AI对战 &#x1f3ae; 欢迎来到这篇MATLAB贪吃蛇编程全攻略&#xff01;本文将带你从零开始&#xff0c;一步步…...

【数据结构与算法】图的基本概念与遍历

目录 一、图的基本概念 1.1 图的基本组成 1.2 图的分类 1.3 顶点的度数 1.4 路径与回路 1.5 子图与特殊图 二. 图的存储结构 2.1 邻接矩阵 2.2 邻接表 三、深度优先遍历 3.1 原理 3.2 实现步骤 3.3 代码实现 四、广度优先遍历 4.1 原理 4.2 实现步骤 4.3 代码…...

MAE自监督大模型在医学报告生成中的应用

MAE自监督大模型在医学报告生成中的应用详解 一、核心技术原理与医学适配 MAE&#xff08;Masked Autoencoder&#xff09;通过掩膜重建策略&#xff0c;在医学影像领域展现出独特优势&#xff1a; 解剖结构理解&#xff1a;通过随机掩盖图像区域&#xff08;如75%的MRI切片&…...

Linux云服务器配置git开发环境

文章目录 1. 安装 git2. git clone3. git add .4. git commit -m 提交记录5. git push&#x1f351; 异常原因&#x1f351; 解决办法 6. git pull7. git log8. git rm9. git mv10. git status 1. 安装 git sudo yum install git -y2. git clone 此命令的作用是从远程仓库把代…...

Vue v-model 深度解析:实现原理与高级用法

一、v-model 的本质 v-model 是 Vue 中最常用的指令之一&#xff0c;它本质上是一个语法糖&#xff0c;用于在表单元素和自定义组件上实现双向数据绑定。在 Vue 2.x 和 Vue 3.x 中&#xff0c;v-model 的实现机制有所不同&#xff0c;但核心思想都是简化数据绑定的过程。 1.1…...

STM32F103单片机在不需要使用 JTAG 调试接口的情况下,释放引脚给其他功能使用。

最近调试STM32F103的时候&#xff0c;由于引脚比较紧张就用了PB3(SYS_JTDO-TRACESWO)引脚&#xff0c;带电下载完程序后&#xff0c;功能都是正常运行&#xff0c;但是断电再上电&#xff0c;PB3引脚就不受控制了&#xff0c;后来查了一下发现PB3不是普通的IO&#xff0c;需要关…...

手机浏览器IP归属地查询全指南:方法与常见问题解答

在当今数字化时代&#xff0c;手机浏览器已成为人们日常生活中不可或缺的工具之一。然而&#xff0c;在使用手机浏览器的过程中&#xff0c;有时我们需要了解当前网络连接的IP归属地信息&#xff0c;那么&#xff0c;手机浏览器IP归属地怎么查看呢&#xff1f;本文将详细介绍几…...

Microsoft Azure DevOps针对Angular项目创建build版本的yaml

Azure DevOps针对Angular项目创建build版本的yaml&#xff0c;并通过变量控制相应job的执行与否。 注意事项&#xff1a;代码前面的空格是通过Tab控制的而不是通过Space控制的。 yaml文件中包含一下内容&#xff1a; 1. 自动触发build 通过指定code branch使提交到此代码库的…...

Web 架构之负载均衡全解析

文章目录 一、引言二、思维导图三、负载均衡的定义与作用定义作用1. 提高可用性2. 增强性能3. 实现扩展性 四、负载均衡类型硬件负载均衡代表设备优缺点 软件负载均衡应用层负载均衡代表软件优缺点 网络层负载均衡代表软件优缺点 五、负载均衡算法轮询算法&#xff08;Round Ro…...

Linux系统管理与编程16:PXE自动化安装部署centos7.9操作系统

兰生幽谷&#xff0c;不为莫服而不芳&#xff1b; 君子行义&#xff0c;不为莫知而止休。 0.准备 1&#xff09;防火墙和SELinux systemctl stop firewalld systemctl disable firewalld setenforce 0 sed -i s/^SELINUX.*/SELINUXdisabled/ /etc/selinux/config (很不好的…...

金丝雀/灰度/蓝绿发布的详解

以下是 金丝雀发布、灰度发布 和 蓝绿发布 的详细解析&#xff0c;涵盖核心原理、技术实现、适用场景及实际案例&#xff1a; 1. 金丝雀发布 (Canary Release) 核心原理 渐进式流量切换&#xff1a;将新版本部署到生产环境后&#xff0c;逐步将用户流量从旧版本迁移到新版本&…...

如何通过ABAP获取SAP生产订单的目标成本

SAP存储生产订单成本的主要底表包括&#xff1a; COBK: CO凭证表头COEP: CO凭证行项目COSS: 来自CO内部的汇总数据COSP: 来自CO外部部的汇总数据 先说结论&#xff1a;SAP 对生产订单的目标成本是没有保存到底表的。那么如何通过代码的方式获取呢&#xff1f; K_KKB_KKBCS_O…...

git 多个提交记录合并为一个

1.场景 有时候用devops等平台测试问题&#xff0c;需要多次修改小的记录提交&#xff0c;但是最终我们在合并主干的时候不想留那么多乱七八糟的记录&#xff0c;就需要在此分支合并这些提交记录&#xff0c;再合并到主干。 2.交互式变基 2.1 确定要合并的提交范围 # 查看最近…...

深入理解栈数据结构(Java实现):从原理到实战应用

在计算机科学的世界里&#xff0c;数据结构是构建高效程序的基石&#xff0c;而栈作为其中最基础且应用广泛的一种数据结构&#xff0c;其独特的 “后进先出&#xff08;LIFO&#xff09;” 特性&#xff0c;使其在众多领域发挥着关键作用。从算法设计到编译器实现&#xff0c;…...

支付宝 SEO 优化:提升小程序曝光与流量的完整指南

在拥有庞大用户基数的支付宝平台上&#xff0c;小程序已成为商家触达用户、提供服务的重要渠道。然而&#xff0c;随着平台上小程序数量的快速增长&#xff0c;如何在激烈的竞争中脱颖而出&#xff0c;获得更多的曝光和流量&#xff0c;成为每个开发者和运营者必须面对的关键挑…...

【leetcode100】最长重复子数组

1、题目描述 给两个整数数组 nums1 和 nums2 &#xff0c;返回 两个数组中 公共的 、长度最长的子数组的长度 。 示例 1&#xff1a; 输入&#xff1a;nums1 [1,2,3,2,1], nums2 [3,2,1,4,7] 输出&#xff1a;3 解释&#xff1a;长度最长的公共子数组是 [3,2,1] 。示例 2&…...

代码随想录算法训练营第五十六天| 图论2—卡码网99. 岛屿数量(dfs bfs)

假期归来继续刷题&#xff0c;图论第二天&#xff0c;主要是进一步熟悉dfs 和 bfs 的运用。 99. 岛屿数量&#xff08;dfs&#xff09; 99. 岛屿数量 ACM模式还是需要练&#xff0c;不过现在输入输出的感觉已经比较熟悉了。首先是要按照输入搭建一个grid&#xff0c;然后有一…...

源码示例:使用SpringBoot+Vue+ElementUI+UniAPP技术组合开发一套小微企业ERP系统

目录 一、系统架构设计 1、技术分层 2、开发环境 二、快速开发实践 1、后端搭建&#xff08;Spring Boot&#xff09; 2、前端管理端&#xff08;VueElementUI&#xff09; 3、移动端开发&#xff08;UniAPP&#xff09; 三、关键集成方案 1、统一接口处理 2、跨平台…...

基于Django框架的股票分红数据爬虫和展示系统

项目截图 一、项目简介 本项目是一个基于 Django 框架的股票分红数据爬虫和展示系统。它可以从东方财富网站爬取股票分红数据&#xff0c;并将数据存储到 Django 数据库中&#xff0c;同时提供数据查询、导出和图表展示功能。该系统为用户提供了一个方便的平台&#xff0c;用于…...

QT高级(1)QTableView自定义委托集合,一个类实现若干委托

自定义委托集合 1同系列文章2 功能3 源码 1同系列文章 QT中级&#xff08;1&#xff09;QTableView自定义委托&#xff08;一&#xff09;实现QSpinBox、QDoubleSpinBox委托 QT中级&#xff08;2&#xff09;QTableView自定义委托&#xff08;二&#xff09;实现QProgressBar委…...

kubectl系列(十一):top 查询pod连接数

在 Kubernetes 中&#xff0c;kubectl top 命令默认仅支持查看 Pod 或节点的 CPU/内存资源使用情况&#xff0c;并不直接提供 TCP 连接数的统计功能。若要获取 Pod 的 TCP 连接数&#xff0c;需结合其他工具和方法。以下是具体实现方案&#xff1a; 1. 直接进入容器查看 TCP 连…...

关于Spring

目录 事务篇 事务篇 先说结论 Spring事务实际上依赖的是Transactional接口和数据库的事务实现。 举个例子说&#xff0c;比如我们现在有一个**Service1类&#xff0c;这个类的方法MethodA执行一个向表A中插入数据&#xff1b;还有一个**Service2类&#xff0c;这个类的方法M…...

小家电专用WD5201 非隔离AC-DC稳压器|宽压80-305V|三档输出2.7/3.3/5V|多重安全保护

小家电专用WD5201 AC-DC稳压器&#xff5c;宽压80-305V&#xff5c;三档输出2.7/3.3/5V&#xff5c;多重安全保护 &#x1f4a5; WD5201&#xff0c;小家电电源的智能“稳压卫士”&#xff01; ✨ 核心卖点&#xff1a; ✅ 宽压兼容&#xff1a;输入 80-305V AC&#xff0c;电网…...