SitePoint 探索 Java 世界:诚邀 Java 开发者投稿
SitePoint 持续拓展内容领域,近期将重点关注 Java。如果您是经验丰富的 Java 开发者,并希望为我们的 Java 内容贡献力量,欢迎联系我们,分享您想撰写的文章主题构想。
Java 中 equals
与 hashCode
方法的正确实现
您已为您的类实现了 equals
方法?很棒!但您也必须实现 hashCode
方法。让我们了解原因以及如何正确实现它。
关键要点:
- 在 Java 中,相等的对象应具有相同的哈希码。因此,如果重写了
equals
方法,则必须创建匹配的hashCode
实现,以确保在基于哈希的集合中存储和检索对象的准确性和一致性。 - 实现
hashCode
时,应使用与equals
方法中使用的相同字段。应尽量避免使用可变字段和集合,因为这可能会导致性能问题。 - 哈希码与性能优化相关,因此除非性能分析表明需要改进,否则不应在哈希上投入过多精力。
- 哈希冲突(两个不同的对象具有相同的哈希码)可以通过改进哈希算法和使用更大的质数作为乘数来减少。这有助于更均匀地将哈希码分布在集合中,从而减少哈希冲突的可能性并确保更快的數據检索。
equals
和 hashCode
方法
虽然 equals
方法从一般角度来看是合理的,但 hashCode
方法则更具技术性。严格来说,它只是一个用于提高性能的实现细节。
大多数数据结构使用 equals
方法来检查它们是否包含某个元素。例如:
List<String> list = Arrays.asList("a", "b", "c"); boolean contains = list.contains("b");
变量 contains
为真,因为虽然 "b" 的实例并不相同(再次忽略字符串驻留),但它们是相等的。
然而,将每个元素与传递给 contains
方法的实例进行比较效率低下,而一类数据结构则使用更高效的方法。它们不将请求的实例与它们包含的每个元素进行比较,而是使用快捷方式来减少可能相等的实例数量,然后只比较这些实例。
这个快捷方式就是哈希码,它可以被视为对象的相等性简化为一个整数值。具有相同哈希码的实例不一定是相等的,但相等的实例具有相同的哈希码。(或者应该具有相同的哈希码,我们稍后将讨论这一点。)此类数据结构通常以其技术名称命名,其名称中包含 "Hash",其中 HashMap
是最著名的代表。
它们通常的工作方式如下:
- 添加元素时,使用其哈希码计算内部数组(称为桶)中的索引。
- 如果其他不相等的元素具有相同的哈希码,则它们最终会进入同一个桶中,并且必须捆绑在一起,例如通过将它们添加到列表中。
- 将实例传递给
contains
方法时,使用其哈希码计算桶。只有其中的元素才会与该实例进行比较。
这样,实现 contains
方法可能只需要很少的,理想情况下不需要任何 equals
比较。
与 equals
方法一样,hashCode
方法也在 Object
类中定义。
关于哈希的思考
如果 hashCode
方法用作确定相等性的快捷方式,那么我们真正应该关心的只有一件事:相等的对象应该具有相同的哈希码。
这也是为什么如果我们重写 equals
方法,就必须创建一个匹配的 hashCode
实现的原因!否则,根据我们的实现相等的事物可能不会具有相同的哈希码,因为它们使用 Object
类的实现。
hashCode
方法的约定
引用源代码:
hashCode
方法的一般约定是:
- 每当在 Java 应用程序的执行过程中多次对同一个对象调用它时,
hashCode
方法必须始终返回相同的整数,前提是没有修改在对象的equals
比较中使用的信息。此整数不必在一个应用程序的执行与同一应用程序的另一个执行之间保持一致。- 如果根据
equals(Object)
方法,两个对象相等,则对这两个对象中的每一个调用hashCode
方法必须产生相同的整数结果。- 如果根据
equals(Object)
方法,两个对象不相等,则不需要调用这两个对象上的hashCode
方法必须产生不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同的整数结果可以提高哈希表的性能。
第一点反映了 equals
方法的一致性属性,第二点是我们上面得出的要求。第三点说明了一个重要的细节,我们稍后将讨论。
实现 hashCode
方法
一个非常简单的 Person.hashCode
实现如下:
List<String> list = Arrays.asList("a", "b", "c"); boolean contains = list.contains("b");
人的哈希码是通过计算相关字段的哈希码并将它们组合起来计算的。两者都留给 Objects
的实用程序函数 hash
来处理。
选择字段
但是哪些字段是相关的呢?这些要求有助于回答这个问题:如果相等的对象必须具有相同的哈希码,那么哈希码计算不应包含任何未用于相等性检查的字段。(否则,只有在这些字段上不同的两个对象将是相等的,但具有不同的哈希码。)
因此,用于哈希的字段集应该是用于相等性的字段集的子集。默认情况下,两者都将使用相同的字段,但有一些细节需要考虑。
一致性
首先,有一致性要求。它应该被相当严格地解释。虽然它允许在某些字段发生更改时哈希码发生更改(对于可变类来说这通常是不可避免的),但哈希数据结构并未为此场景做好准备。
正如我们上面所看到的,哈希码用于确定元素的桶。但是,如果哈希相关字段发生更改,则不会重新计算哈希,并且不会更新内部数组。
这意味着使用相等的对象或甚至使用完全相同的实例的后续查询将失败!数据结构计算当前的哈希码(与用于存储实例的哈希码不同),并在错误的桶中查找。
结论:最好不要使用可变字段进行哈希码计算!
性能
哈希码的计算次数可能与 equals
方法的调用次数大致相同。这很可能发生在代码的关键性能部分,因此考虑性能是有意义的。并且与 equals
方法不同,这里有更多空间进行优化。
除非使用复杂的算法或涉及许多字段,否则组合其哈希码的算术成本与不可避免的成本一样微不足道。但是应该考虑是否需要将所有字段都包含在计算中!特别是应该对集合持怀疑态度。例如,列表和集合将为它们的每个元素计算哈希值。是否需要调用它们应该根据具体情况进行考虑。
如果性能至关重要,使用 Objects.hash
也可能不是最佳选择,因为它需要为其可变参数创建数组。
但是关于优化的通用规则仍然适用:不要过早优化!使用常见的哈希码算法,也许放弃包含集合,并且只有在性能分析显示存在改进的可能性后才进行优化。
冲突
全力以赴追求性能,那么这个实现怎么样?
List<String> list = Arrays.asList("a", "b", "c"); boolean contains = list.contains("b");
它肯定很快。并且相等的对象将具有相同的哈希码,所以我们在这方面也很好。作为奖励,没有涉及可变字段!
但是请记住我们之前关于桶的内容?这样所有实例都将进入同一个桶!这通常会导致一个链表保存所有元素,这对性能来说非常糟糕。例如,每个 contains
调用都会触发链表的线性扫描。
因此,我们希望尽可能减少同一个桶中的项目数量!即使对于非常相似的对象,也能返回差异很大的哈希码的算法是一个良好的开端。如何实现部分取决于所选字段。我们在计算中包含的细节越多,哈希码不同的可能性就越大。请注意,这与我们对性能的想法完全相反。因此,有趣的是,使用过多或过少的字段都可能导致性能不佳。
防止冲突的另一部分是用于实际计算哈希的算法。
计算哈希值
计算字段哈希码的最简单方法是对其调用 hashCode
方法。可以手动组合它们。一个常见的算法是从某个任意数字开始,然后重复地将其与另一个数字(通常是一个小的质数)相乘,然后再添加字段的哈希值:
List<String> list = Arrays.asList("a", "b", "c"); boolean contains = list.contains("b");
这可能会导致溢出,但这在 Java 中不会导致异常,因此问题不大。
请注意,即使是优秀的哈希算法,如果输入数据具有特定模式,也可能导致异常频繁的冲突。作为一个简单的例子,假设我们通过添加点的 x 和 y 坐标来计算点的哈希值。这听起来还不错,直到我们意识到我们经常处理直线 f(x) = -x 上的点,这意味着对于所有这些点,x y == 0。冲突,很多!
但是再次强调:使用常见的算法,并且除非性能分析显示存在问题,否则不要担心。
总结
我们已经看到,计算哈希码就像将相等性压缩为整数值:相等的对象必须具有相同的哈希码,并且出于性能原因,最好尽可能少的不相等对象共享相同的哈希码。
这意味着如果重写了 equals
方法,则必须始终重写 hashCode
方法。
实现 hashCode
方法时:
- 使用与
equals
方法中使用的相同字段(或其子集)。 - 最好不要包含可变字段。
- 考虑不调用集合上的
hashCode
方法。 - 使用常见的算法,除非输入数据的模式与之相反。
记住,hashCode
方法与性能有关,因此除非性能分析表明有必要,否则不要浪费太多精力。
关于正确实现 Java hashCode
方法的常见问题解答 (FAQ)
Java 中 hashCode()
方法的意义是什么?
Java 中的 hashCode()
方法是一个内置函数,它返回一个整数值。它主要用于基于哈希的集合(如 HashMap
、HashSet
和 HashTable
)以更有效地存储和检索对象。hashCode()
方法与 equals()
方法协同工作,以确保每个对象都有一个唯一的标识符。这有助于快速检索数据,尤其是在大型集合中,从而提高 Java 应用程序的性能。
Java 中 hashCode()
方法是如何工作的?
Java 中的 hashCode()
方法的工作原理是生成一个整数值,该值表示对象的内存地址。此值用作对象在基于哈希的集合中的索引号。当您对对象调用 hashCode()
方法时,它会使用哈希算法来生成此唯一整数。但是,需要注意的是,两个不同的对象可能具有相同的 hashCode
,这被称为哈希冲突。
Java 中 equals()
和 hashCode()
方法之间的约定是什么?
Java 中 equals()
和 hashCode()
方法之间的约定是一组规则,用于管理它们的交互。该约定指出,如果根据 equals()
方法,两个对象相等,则对这两个对象中的每一个调用 hashCode()
方法必须产生相同的整数结果。这确保了在基于哈希的集合中存储和检索对象时的一致性和准确性。
如何在 Java 中重写 hashCode()
方法?
在 Java 中重写 hashCode()
方法包括提供您自己的实现,该实现为每个对象返回一个唯一的整数。这可以通过使用对象的实例变量和质数乘数来实现。质数有助于将哈希码均匀地分布在集合中,从而减少哈希冲突的可能性。
什么是哈希冲突,如何避免?
哈希冲突是指 hashCode()
方法为两个不同的对象生成相同的整数。如果处理不当,这可能会导致数据丢失。为了避免哈希冲突,您可以改进哈希算法以生成更多唯一的整数。此外,使用更大的质数作为乘数可以帮助更均匀地将哈希码分布在集合中。
为什么应该重写 hashCode()
方法?
重写 hashCode()
方法可以提高 Java 应用程序的性能,尤其是在处理大型集合时。通过提供您自己的实现,您可以生成更唯一且更均匀分布的哈希码,从而减少哈希冲突的可能性并确保更快的數據检索。
在 Java 中,两个不相等的对象可以具有相同的 hashCode
吗?
是的,在 Java 中,两个不相等的对象可以具有相同的 hashCode
。这被称为哈希冲突。但是,通过改进哈希算法和使用更大的质数作为乘数,可以降低这种情况发生的可能性。
如果我不重写 hashCode()
方法会发生什么?
如果您不重写 hashCode()
方法,Java 将使用其默认实现,这可能无法为每个对象提供唯一的哈希码。这可能导致哈希冲突和基于哈希的集合中更慢的数据检索。
hashCode()
方法如何提高 Java 应用程序的性能?
hashCode()
方法通过为每个对象提供唯一的标识符来提高 Java 应用程序的性能。这允许在基于哈希的集合中更快地检索数据,因为可以使用对象的哈希码直接找到该对象,而无需搜索整个集合。
我可以在非基于哈希的集合中使用 hashCode()
方法吗?
虽然 hashCode()
方法主要用于基于哈希的集合,但它也可以用于非基于哈希的集合。但是,其好处可能不那么明显,因为非基于哈希的集合不依赖于哈希码进行数据存储和检索。
以上是如何正确实现java s HashCode的详细内容。更多信息请关注PHP中文网其他相关文章!

中国女性科技力量在AI领域的崛起:荣耀与DeepSeek合作背后的女性故事女性在科技领域的贡献日益显着。中国科技部数据显示,女性科技工作者数量庞大,在AI算法开发中展现出独特的社会价值敏感性。本文将聚焦荣耀手机,探究其率先接入DeepSeek大模型背后的女性团队力量,展现她们如何推动科技进步,重塑科技发展价值坐标系。 2024年2月8日,荣耀正式上线DeepSeek-R1满血版大模型,成为安卓阵营首家接入DeepSeek的厂商,引发用户热烈反响。这一成功背后,女性团队成员在产品决策、技术攻坚和用户

DeepSeek公司在知乎发布技术文章,详细介绍了其DeepSeek-V3/R1推理系统,并首次公开关键财务数据,引发业界关注。文章显示,该系统单日成本利润率高达545%,创下全球AI大模型盈利新高。DeepSeek的低成本策略使其在市场竞争中占据优势。其模型训练成本仅为同类产品的1%-5%,V3模型训练成本仅为557.6万美元,远低于竞争对手。同时,R1的API定价仅为OpenAIo3-mini的1/7至1/2。这些数据证明了DeepSeek技术路线的商业可行性,也为AI大模型的高效盈利树立了

美的即将发布搭载DeepSeek大模型的首款空调——美的鲜净感空气机T6,发布会定于3月1日下午1点30分举行。这款空调配备先进的空气智驾系统,可根据环境智能调节温度、湿度和风速等参数。更重要的是,它集成了DeepSeek大模型,支持超过40万条AI语音指令。美的此举引发业界热议,尤其关注白电产品与大模型结合的意义。不同于传统空调简单的温度设定,美的鲜净感空气机T6能够理解更复杂、更模糊的指令,并根据家庭环境智能调节湿度等,显着提升用户体验。

网站建设只是第一步:SEO与反向链接的重要性 建立网站只是将其转化为宝贵营销资产的第一步。您需要进行SEO优化,以提高网站在搜索引擎中的可见度,吸引潜在客户。反向链接是提升网站排名的关键,它向谷歌和其他搜索引擎表明您的网站权威性和可信度。 并非所有反向链接都有利:识别并避免有害链接 并非所有反向链接都有益。有害链接会损害您的排名。优秀的免费反向链接检查工具可以监控链接到您网站的来源,并提醒您注意有害链接。此外,您还可以分析竞争对手的链接策略,从中学习借鉴。 免费反向链接检查工具:您的SEO情报员

DeepSeek-R1赋能百度文库与网盘:深度思考与行动的完美融合短短一个月内,DeepSeek-R1已迅速融入众多平台。百度凭借大胆的战略布局,将DeepSeek作为第三方模型伙伴,整合进自身生态系统,这标志着其“大模型 搜索”生态战略的重大进展。百度搜索和文心智能体平台率先接入DeepSeek及文心大模型的深度搜索功能,为用户提供免费的AI搜索体验。同时,“百度一下,你就知道”的经典slogan回归,新版百度APP也整合了文心大模型和DeepSeek的能力,推出“AI搜索”、“全网信息提炼”

此基于GO的网络漏洞扫描仪有效地确定了潜在的安全弱点。 它利用了GO的并发功能的速度功能,包括服务检测和漏洞匹配。让我们探索它的能力和道德

AI及时工程代码生成:开发人员指南 代码开发的景观有望进行重大转变。 掌握大型语言模型(LLM)和及时工程对于未来几年对开发人员至关重要。 Th


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

Atom编辑器mac版下载
最流行的的开源编辑器

Dreamweaver Mac版
视觉化网页开发工具

VSCode Windows 64位 下载
微软推出的免费、功能强大的一款IDE编辑器

适用于 Eclipse 的 SAP NetWeaver 服务器适配器
将Eclipse与SAP NetWeaver应用服务器集成。

EditPlus 中文破解版
体积小,语法高亮,不支持代码提示功能