Java高效编程(14):考虑实现 `Comparable
解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界
与其他方法不同,compareTo 并非 Object 类中声明的,而是 Comparable 接口的唯一方法。compareTo 方法与 equals 类似,但它不仅支持相等性比较,还允许顺序比较,同时它是泛型的。通过实现 Comparable 接口,一个类表明其实例具有自然顺序。这使得对实现 Comparable 的对象数组进行排序变得非常简单:
Arrays.sort(a);
使用 Comparable 接口,可以轻松地搜索、计算极值或维护自动排序的集合。例如,以下程序利用 String 实现了 Comparable,它打印出一个按字母顺序排列的命令行参数列表,并去除重复项:
public class WordList {public static void main(String[] args) {Set<String> s = new TreeSet<>();Collections.addAll(s, args);System.out.println(s);}
}
通过实现 Comparable,你的类可以与依赖该接口的各种通用算法和集合实现进行互操作。实现 Comparable 所需的工作量非常小,但却带来了巨大的收益。几乎所有 Java 平台库中的值类以及所有枚举类型(详见【条目34】)都实现了 Comparable。如果你编写的值类有明显的自然排序,例如字母顺序、数字顺序或时间顺序,那么你应该实现 Comparable 接口。
Comparable 接口声明如下:
public interface Comparable<T> {int compareTo(T t);
}
compareTo 方法的通用合同
compareTo 方法的通用合同类似于 equals 的合同:
- 比较当前对象与指定对象的顺序,返回负整数、零或正整数,分别表示当前对象小于、等于或大于指定对象。
- 如果两个对象具有不同类型,通常会抛出
ClassCastException。
这个合同的数学符号表示如下:
- 对所有
x和y,应确保sgn(x.compareTo(y)) == -sgn(y.compareTo(x)),这意味着x.compareTo(y)只有在y.compareTo(x)抛出异常时才会抛出异常。 - 应确保传递性:如果
x.compareTo(y) > 0 && y.compareTo(z) > 0,则x.compareTo(z) > 0。 - 如果
x.compareTo(y) == 0,则sgn(x.compareTo(z)) == sgn(y.compareTo(z))对所有z应成立。
此外,推荐但不强制要求 x.compareTo(y) == 0 等价于 x.equals(y)。如果违背了这一点,应在类文档中注明该类的自然顺序与 equals 不一致。
比较与 equals 的一致性
compareTo 方法的比较应符合 equals 的等价性、对称性和传递性原则。如果违反这些原则,可能会导致依赖比较的类(如 TreeSet、TreeMap)出错。虽然不致命,但会导致结果不一致。例如,BigDecimal 类的 compareTo 方法与其 equals 方法不一致。对于 HashSet,new BigDecimal("1.0") 和 new BigDecimal("1.00") 被视为不相等,而在 TreeSet 中则视为相等。
编写 compareTo 方法
编写 compareTo 方法类似于编写 equals 方法,但有一些关键区别。由于 Comparable 是参数化接口,因此 compareTo 方法是静态类型化的,避免了类型检查和强制转换。如果参数类型错误,代码甚至无法编译。
在 compareTo 方法中,字段是按顺序比较的。对于对象引用字段,可以递归调用 compareTo 方法。如果字段没有实现 Comparable,或者需要非标准排序,可以使用 Comparator。例如,下面是一个比较 CaseInsensitiveString 类的 compareTo 方法:
// 使用对象引用字段的单字段 Comparable
public final class CaseInsensitiveString implements Comparable<CaseInsensitiveString> {public int compareTo(CaseInsensitiveString cis) {return String.CASE_INSENSITIVE_ORDER.compare(s, cis.s);}// 其他代码省略
}
使用比较器构造方法实现 compareTo
在 Java 8 中,Comparator 接口提供了一组构造方法来简洁地构建比较器。下面是使用比较器构造方法实现 PhoneNumber 类 compareTo 的例子:
// 使用比较器构造方法的 Comparable
private static final Comparator<PhoneNumber> COMPARATOR =comparingInt((PhoneNumber pn) -> pn.areaCode).thenComparingInt(pn -> pn.prefix).thenComparingInt(pn -> pn.lineNum);public int compareTo(PhoneNumber pn) {return COMPARATOR.compare(this, pn);
}
这种方法通过使用 Comparator 的 comparingInt 和 thenComparingInt 方法来简洁地构建比较逻辑。
避免基于差值的比较器
不要使用基于两个值差值的比较器,例如:
// 错误的差值比较器 - 违反传递性
static Comparator<Object> hashCodeOrder = new Comparator<>() {public int compare(Object o1, Object o2) {return o1.hashCode() - o2.hashCode();}
};
这种方法容易受到整数溢出和浮点运算误差的影响。相反,应该使用静态比较方法或比较器构造方法,例如:
// 基于静态比较方法的比较器
static Comparator<Object> hashCodeOrder = new Comparator<>() {public int compare(Object o1, Object o2) {return Integer.compare(o1.hashCode(), o2.hashCode());}
};
或:
// 基于比较器构造方法的比较器
static Comparator<Object> hashCodeOrder = Comparator.comparingInt(o -> o.hashCode());
总结
当你实现具有合理顺序的值类时,应该让该类实现 Comparable 接口,以便能够轻松排序、搜索和在集合中使用。避免在 compareTo 方法中使用 < 和 > 操作符,推荐使用 Java 提供的静态比较方法或比较器构造方法。
相关文章:
Java高效编程(14):考虑实现 `Comparable
解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 与其他方法不同,compareTo 并非 Object 类中声明的,而是 Comparable 接口的唯一方法。compareTo 方法与 equals 类似,但它不仅支持相等性比较,还允许顺序…...
华为昇腾CANN训练营2024第二季--Ascend C算子开发能力认证(中级)题目和经验分享
大家好,我是刘明,明志科技创始人,华为昇思MindSpore布道师。 技术上主攻前端开发、鸿蒙开发和AI算法研究。 努力为大家带来持续的技术分享,如果你也喜欢我的文章,就点个关注吧 正文开始 华为昇腾CANN训练营2024第二季…...
实战OpenCV之形态学操作
基础入门 形态学操作是一种基于图像形状的处理方法,主要用于结构分析,比如:边缘检测、轮廓提取、噪声去除等。这些操作通常使用一个称为“结构元素”(Structuring Element)的核来进行,结构元素可以是任何形状,但最常见的有矩形和圆形。形态学操作的核心在于通过结构元素…...
矩阵的特征值和特征向量
矩阵的特征值和特征向量是线性代数中非常重要的概念,用于描述矩阵对向量的作用,特别是在矩阵对向量的线性变换中的表现。它们帮助我们理解矩阵在某些方向上的缩放或旋转效果。 1. 特征值和特征向量的定义: 给定一个 n n n \times n nn 的…...
(11)MATLAB莱斯(Rician)衰落信道仿真2
文章目录 前言一、莱斯衰落信道仿真模型二、仿真代码与结果1.仿真代码2.仿真结果画图 三、后续:四、参考文献: 前言 首先给出莱斯衰落信道仿真模型,该模型由直射路径分量和反射路径分量组成,其中反射路径分量由瑞利衰落信道模型构…...
ComfyUI局部重绘换衣讲解
一、下载插件 ComfyUI-Impact-Pack 下载地址 https://github.com/ltdrdata/ComfyUI-Impact-Pack 主要用到sam Detector去绘制衣服蒙版和高斯模糊蒙版,高斯模糊让蒙版边缘更加柔和 sams模型 放在E:\Comfyui\ComfyUI\models\sams二、换衣思路 文生图或直接上传…...
Android——添加联系人
概述 方式一:使用ContentResolver多次写入,每次写入一个字段 第一步 往手机联系人应用中的raw_contacts表添加一条记录 raw_contacts表 ContentValues values new ContentValues();// 往 raw_contacts 添加联系人记录,并获取添加后的联…...
高级 Java Redis 客户端 有哪些?
高级Java Redis客户端主要包括以下几种: 1. Redisson (https://github.com/redisson/redisson) 特点:Redisson是一个在Redis的基础上实现的Java驻留数据网格(In-Memory Data Grid)。它不仅是一个Redis的J…...
jenkins项目发布基础
随着软件开发需求及复杂度的不断提高,团队开发成员之间如何更好地协同工作以确保软件开发的质量已经慢慢成为开发过程中不可回避的问题。Jenkins 自动化部署可以解决集成、测试、部署等重复性的工作,工具集成的效率明显高于人工操作;并且持续集成可以更早的获取代码变更的信息,…...
前缀和算法详解
对于查询区间和的问题,可以预处理出来一个前缀和数组 dp,数组中存储的是从下标 0 的位置到当前位置的区间和,这样只需要通过前缀和数组就可以快速的求出指定区间的和了,例如求 l ~ r 区间的和,就可以之间使用 dp[l - 1…...
Android-Handle消息传递和线程通信
本文为作者学习笔记,如有误,请各位大佬指点 目录 一、同步异步 二、Java多线程通信 三、Handler是什么 四、Handler相关的类 五、Handler常用方法 1. 发送消息 2. 接收处理消息 3. 切换线程 六、使用Handler 使用Handler更新UI 使用Handler延…...
【Kubernetes】常见面试题汇总(四十七)
目录 106.考虑一种情况,公司希望通过保持最低成本来提高效率和技术运营速度。您如何看待公司将如何实现这一目标? 107.假设一家公司想要修改其部署方法,并希望构建一个可扩展性和响应性更高的平台。您如何看待这家公司能够实现这一目标以满足…...
grafana全家桶-loki promtail收集k8s容器日志
loki是grafana旗下轻量级日志收集工具,为了减少loki对集群的影响,把loki的agent日志收集端promtail部署在k8s集群中,loki server部署在集群外面。这样简单做一个解耦,避免大量读写的应用影响到集群内业务服务。 一、promtail部署…...
HTML5+CSS+JavaScript剪子石头布游戏
HTML5CSSJavaScript剪子石头布游戏 用HTML5CSSJavaScript剪子石头布游戏实现剪子石头布游戏,游戏有成绩计数,人、机输赢情况,及平局情况。 ✂代表剪刀,▉代表石头,▓ 代表布,给出人机双方的出拳情况 游戏…...
Flask-3
文章目录 ORMFlask-SQLAlchemySQLAlchemy中的session对象数据库连接设置常用的SQLAlchemy字段类型常用的SQLAlchemy列约束选项 数据库基本操作模型类定义 数据表操作创建和删除表 数据操作基本查询SQLAlchemy常用的查询过滤器SQLAlchemy常用的查询结果方法多条件查询分页器聚合…...
Redis的基本使用
简介 传统的数据库是 关系数据库,但是Redis是键值对数据库传统的数据库是基于 磁盘存储的,但是Redis是基于 内存存储的 基于内存,读写性能更高内存是不大的,只能存储热点信息 安装 绿色软件,安装即可使用 安装服务 手…...
[241004] Linux 系统中配置文件的区别 | VirtualBox 7.1.2 发布,修复多项问题并提升性能
目录 Linux 系统中 /etc/profile, ~/.bash_profile, ~/.profile, ~/.bashrc 等配置文件的区别一、配置文件类型二、配置文件作用三、交互式登录 Shell 和非登录 Shell交互式登录 shell交互式非登录 shell 四、配置文件加载顺序五、~/.bash_profile 和 ~/.bashrc 的区别 Virtual…...
hbuilderx+uniapp+Android宠物用品商城领养服务系统的设计与实现 微信小程序沙箱支付
目录 项目介绍支持以下技术栈:具体实现截图HBuilderXuniappmysql数据库与主流编程语言java类核心代码部分展示登录的业务流程的顺序是:数据库设计性能分析操作可行性技术可行性系统安全性数据完整性软件测试详细视频演示源码获取方式 项目介绍 顾客 领养…...
SVN 迁移到 GIT,并保留提交记录
1)svn账号与git账号映射 创建 user.txt ,格式如下,user.txt 放置在git base here 所选目录下即可 schacon Scott Chacon <schacongeemail.com> selse Someo Nelse <selsegeemail.com> 为了获得 SVN 使用的作者名字列表…...
【数据结构与算法】LeetCode:堆和快排
文章目录 LeetCode:堆和快排排序数组数组中的第K个最大元素 (Hot 100)前 K 个高频元素(Hot 100)数据流的中位数(Hot 100) LeetCode:堆和快排 排序数组 排序数组 双向切分实现快排…...
5大核心模块构建学术排版系统:STIX Two字体全面应用指南
5大核心模块构建学术排版系统:STIX Two字体全面应用指南 【免费下载链接】stixfonts OpenType Unicode fonts for Scientific, Technical, and Mathematical texts 项目地址: https://gitcode.com/gh_mirrors/st/stixfonts 一、价值解析:为什么专…...
SEO最常用的工具有哪些_新手SEO如何选择工具
SEO最常用的工具有哪些?新手SEO如何选择工具 在当今的数字化时代,SEO(搜索引擎优化)已成为提升网站流量和品牌知名度的关键手段。作为新手SEO,你可能会遇到一个重要的问题:SEO最常用的工具有哪些ÿ…...
Pandas 操作指南(一):DataFrame 的构建与表格数据组织
在数据分析与数据处理中,原始数据往往并不是一开始就以规范表格的形式出现。它可能来自列表(list)、字典(dict)、CSV/Excel 文件,或程序运行过程中临时生成的数据集合。若这些数据尚未被整理为结构明确的表…...
微信小程序端集成实践:打造手机上的国风绘画工具
微信小程序端集成实践:打造手机上的国风绘画工具 想不想随时随地,掏出手机就能创作一幅充满诗意的国风画作?以前这可能需要多年的绘画功底,但现在,借助AI的力量,每个人都能成为自己手机里的国风画师。今天…...
ipa 覆盖算法参数调优实战:从理论到可视化验证
1. IPA覆盖算法核心参数解析 在机器人路径规划领域,IPA覆盖算法因其高效性和适应性被广泛应用。这个算法的核心在于几个关键参数的协同作用,它们直接影响着机器人的覆盖路径质量和执行效率。让我们先来认识这些"幕后操控者": cover…...
【bilibili-downloader】:突破4K画质限制的B站视频下载工具:给视频收藏爱好者的高效解决方案
【bilibili-downloader】:突破4K画质限制的B站视频下载工具:给视频收藏爱好者的高效解决方案 【免费下载链接】bilibili-downloader B站视频下载,支持下载大会员清晰度4K,持续更新中 项目地址: https://gitcode.com/gh_mirrors/…...
SAP-ABAP:SAP ABAP 经典弹窗函数 POPUP_TO_CONFIRM 完全指南
SAP ABAP 经典弹窗函数 POPUP_TO_CONFIRM 完全指南在SAP开发中,如何优雅地让用户确认“你确定要删除这条数据吗?”——答案就是 POPUP_TO_CONFIRM。在 ABAP 开发的世界里,与用户的交互不仅仅是输入输出。很多时候,我们需要在程序执…...
5 鸿蒙应用权限配置快速落地实操 | 鸿蒙开发筑基实战
鸿蒙应用权限配置快速落地实操 | 鸿蒙开发筑基实战 作者:杨建宾(华夏之光永存) 摘要 本文面向鸿蒙开发新手与普通工程师,详细讲解鸿蒙应用权限配置的完整实操流程,包含权限分类、配置文件声明、运行时申请、权限校验等…...
ACUITY IMAGING 070-200000控制器模块
ACUITY IMAGING 070-200000 控制 / 模拟模块ACUITY IMAGING 070-200000 是美国 ACUITY IMAGING 公司出品的工业级高精度信号处理与控制模块,主要用于机器视觉、自动化检测及精密成像系统,负责信号采集、逻辑控制与数据传输,是工业视觉系统的核…...
郭老师-永远要跟认知比你高的人在一起
永远要跟认知比你高的人在一起 ——从高人身上汲取智慧“你跟什么样的人在一起, 比你做什么样的事情重要得多。” ——巴菲特🌿 真正的成长, 不是埋头苦干, 而是—— 站在巨人的肩膀上看世界。🔭 一、认知高的人&#…...
