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

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

这个合同的数学符号表示如下:

  • 对所有 xy,应确保 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 的等价性、对称性和传递性原则。如果违反这些原则,可能会导致依赖比较的类(如 TreeSetTreeMap)出错。虽然不致命,但会导致结果不一致。例如,BigDecimal 类的 compareTo 方法与其 equals 方法不一致。对于 HashSetnew 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 接口提供了一组构造方法来简洁地构建比较器。下面是使用比较器构造方法实现 PhoneNumbercompareTo 的例子:

// 使用比较器构造方法的 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);
}

这种方法通过使用 ComparatorcomparingIntthenComparingInt 方法来简洁地构建比较逻辑。

避免基于差值的比较器

不要使用基于两个值差值的比较器,例如:

// 错误的差值比较器 - 违反传递性
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编程的无限可能&#xff1a;《奇妙的Python》带你漫游代码世界 与其他方法不同&#xff0c;compareTo 并非 Object 类中声明的&#xff0c;而是 Comparable 接口的唯一方法。compareTo 方法与 equals 类似&#xff0c;但它不仅支持相等性比较&#xff0c;还允许顺序…...

华为昇腾CANN训练营2024第二季--Ascend C算子开发能力认证(中级)题目和经验分享

大家好&#xff0c;我是刘明&#xff0c;明志科技创始人&#xff0c;华为昇思MindSpore布道师。 技术上主攻前端开发、鸿蒙开发和AI算法研究。 努力为大家带来持续的技术分享&#xff0c;如果你也喜欢我的文章&#xff0c;就点个关注吧 正文开始 华为昇腾CANN训练营2024第二季…...

实战OpenCV之形态学操作

基础入门 形态学操作是一种基于图像形状的处理方法,主要用于结构分析,比如:边缘检测、轮廓提取、噪声去除等。这些操作通常使用一个称为“结构元素”(Structuring Element)的核来进行,结构元素可以是任何形状,但最常见的有矩形和圆形。形态学操作的核心在于通过结构元素…...

矩阵的特征值和特征向量

矩阵的特征值和特征向量是线性代数中非常重要的概念&#xff0c;用于描述矩阵对向量的作用&#xff0c;特别是在矩阵对向量的线性变换中的表现。它们帮助我们理解矩阵在某些方向上的缩放或旋转效果。 1. 特征值和特征向量的定义&#xff1a; 给定一个 n n n \times n nn 的…...

(11)MATLAB莱斯(Rician)衰落信道仿真2

文章目录 前言一、莱斯衰落信道仿真模型二、仿真代码与结果1.仿真代码2.仿真结果画图 三、后续&#xff1a;四、参考文献&#xff1a; 前言 首先给出莱斯衰落信道仿真模型&#xff0c;该模型由直射路径分量和反射路径分量组成&#xff0c;其中反射路径分量由瑞利衰落信道模型构…...

ComfyUI局部重绘换衣讲解

一、下载插件 ComfyUI-Impact-Pack 下载地址 https://github.com/ltdrdata/ComfyUI-Impact-Pack 主要用到sam Detector去绘制衣服蒙版和高斯模糊蒙版&#xff0c;高斯模糊让蒙版边缘更加柔和 sams模型 放在E:\Comfyui\ComfyUI\models\sams二、换衣思路 文生图或直接上传…...

Android——添加联系人

概述 方式一&#xff1a;使用ContentResolver多次写入&#xff0c;每次写入一个字段 第一步 往手机联系人应用中的raw_contacts表添加一条记录 raw_contacts表 ContentValues values new ContentValues();// 往 raw_contacts 添加联系人记录&#xff0c;并获取添加后的联…...

高级 Java Redis 客户端 有哪些?

高级Java Redis客户端主要包括以下几种&#xff1a; 1. Redisson &#xff08;https://github.com/redisson/redisson&#xff09; 特点&#xff1a;Redisson是一个在Redis的基础上实现的Java驻留数据网格&#xff08;In-Memory Data Grid&#xff09;。它不仅是一个Redis的J…...

jenkins项目发布基础

随着软件开发需求及复杂度的不断提高,团队开发成员之间如何更好地协同工作以确保软件开发的质量已经慢慢成为开发过程中不可回避的问题。Jenkins 自动化部署可以解决集成、测试、部署等重复性的工作,工具集成的效率明显高于人工操作;并且持续集成可以更早的获取代码变更的信息,…...

前缀和算法详解

对于查询区间和的问题&#xff0c;可以预处理出来一个前缀和数组 dp&#xff0c;数组中存储的是从下标 0 的位置到当前位置的区间和&#xff0c;这样只需要通过前缀和数组就可以快速的求出指定区间的和了&#xff0c;例如求 l ~ r 区间的和&#xff0c;就可以之间使用 dp[l - 1…...

Android-Handle消息传递和线程通信

本文为作者学习笔记&#xff0c;如有误&#xff0c;请各位大佬指点 目录 一、同步异步 二、Java多线程通信 三、Handler是什么 四、Handler相关的类 五、Handler常用方法 1. 发送消息 2. 接收处理消息 3. 切换线程 六、使用Handler 使用Handler更新UI 使用Handler延…...

【Kubernetes】常见面试题汇总(四十七)

目录 106.考虑一种情况&#xff0c;公司希望通过保持最低成本来提高效率和技术运营速度。您如何看待公司将如何实现这一目标&#xff1f; 107.假设一家公司想要修改其部署方法&#xff0c;并希望构建一个可扩展性和响应性更高的平台。您如何看待这家公司能够实现这一目标以满足…...

grafana全家桶-loki promtail收集k8s容器日志

loki是grafana旗下轻量级日志收集工具&#xff0c;为了减少loki对集群的影响&#xff0c;把loki的agent日志收集端promtail部署在k8s集群中&#xff0c;loki server部署在集群外面。这样简单做一个解耦&#xff0c;避免大量读写的应用影响到集群内业务服务。 一、promtail部署…...

HTML5+CSS+JavaScript剪子石头布游戏

HTML5CSSJavaScript剪子石头布游戏 用HTML5CSSJavaScript剪子石头布游戏实现剪子石头布游戏&#xff0c;游戏有成绩计数&#xff0c;人、机输赢情况&#xff0c;及平局情况。 ✂代表剪刀&#xff0c;▉代表石头&#xff0c;▓ 代表布&#xff0c;给出人机双方的出拳情况 游戏…...

Flask-3

文章目录 ORMFlask-SQLAlchemySQLAlchemy中的session对象数据库连接设置常用的SQLAlchemy字段类型常用的SQLAlchemy列约束选项 数据库基本操作模型类定义 数据表操作创建和删除表 数据操作基本查询SQLAlchemy常用的查询过滤器SQLAlchemy常用的查询结果方法多条件查询分页器聚合…...

Redis的基本使用

简介 传统的数据库是 关系数据库&#xff0c;但是Redis是键值对数据库传统的数据库是基于 磁盘存储的&#xff0c;但是Redis是基于 内存存储的 基于内存&#xff0c;读写性能更高内存是不大的&#xff0c;只能存储热点信息 安装 绿色软件&#xff0c;安装即可使用 安装服务 手…...

[241004] Linux 系统中配置文件的区别 | VirtualBox 7.1.2 发布,修复多项问题并提升性能

目录 Linux 系统中 /etc/profile, ~/.bash_profile, ~/.profile, ~/.bashrc 等配置文件的区别一、配置文件类型二、配置文件作用三、交互式登录 Shell 和非登录 Shell交互式登录 shell交互式非登录 shell 四、配置文件加载顺序五、~/.bash_profile 和 ~/.bashrc 的区别 Virtual…...

hbuilderx+uniapp+Android宠物用品商城领养服务系统的设计与实现 微信小程序沙箱支付

目录 项目介绍支持以下技术栈&#xff1a;具体实现截图HBuilderXuniappmysql数据库与主流编程语言java类核心代码部分展示登录的业务流程的顺序是&#xff1a;数据库设计性能分析操作可行性技术可行性系统安全性数据完整性软件测试详细视频演示源码获取方式 项目介绍 顾客 领养…...

SVN 迁移到 GIT,并保留提交记录

1&#xff09;svn账号与git账号映射 创建 user.txt &#xff0c;格式如下&#xff0c;user.txt 放置在git base here 所选目录下即可 schacon Scott Chacon <schacongeemail.com> selse Someo Nelse <selsegeemail.com> 为了获得 SVN 使用的作者名字列表&#xf…...

【数据结构与算法】LeetCode:堆和快排

文章目录 LeetCode&#xff1a;堆和快排排序数组数组中的第K个最大元素 &#xff08;Hot 100&#xff09;前 K 个高频元素&#xff08;Hot 100&#xff09;数据流的中位数&#xff08;Hot 100&#xff09; LeetCode&#xff1a;堆和快排 排序数组 排序数组 双向切分实现快排…...

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…...

OpenLayers 可视化之热力图

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 热力图&#xff08;Heatmap&#xff09;又叫热点图&#xff0c;是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...

STM32F4基本定时器使用和原理详解

STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...

【HTML-16】深入理解HTML中的块元素与行内元素

HTML元素根据其显示特性可以分为两大类&#xff1a;块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...

CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云

目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...

vue3+vite项目中使用.env文件环境变量方法

vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量&#xff0c;这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...

【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统

目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索&#xff08;基于物理空间 广播范围&#xff09;2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...

代码规范和架构【立芯理论一】(2025.06.08)

1、代码规范的目标 代码简洁精炼、美观&#xff0c;可持续性好高效率高复用&#xff0c;可移植性好高内聚&#xff0c;低耦合没有冗余规范性&#xff0c;代码有规可循&#xff0c;可以看出自己当时的思考过程特殊排版&#xff0c;特殊语法&#xff0c;特殊指令&#xff0c;必须…...

vue3 daterange正则踩坑

<el-form-item label"空置时间" prop"vacantTime"> <el-date-picker v-model"form.vacantTime" type"daterange" start-placeholder"开始日期" end-placeholder"结束日期" clearable :editable"fal…...

第一篇:Liunx环境下搭建PaddlePaddle 3.0基础环境(Liunx Centos8.5安装Python3.10+pip3.10)

第一篇&#xff1a;Liunx环境下搭建PaddlePaddle 3.0基础环境&#xff08;Liunx Centos8.5安装Python3.10pip3.10&#xff09; 一&#xff1a;前言二&#xff1a;安装编译依赖二&#xff1a;安装Python3.10三&#xff1a;安装PIP3.10四&#xff1a;安装Paddlepaddle基础框架4.1…...