出自《java puzzle》
下面的程序包含了一个简单的不可变类,它表示一个名字,其main方法将一个名字置于一个集合中,并检查该集合是否确实包含了该名字。那么,这个程序到底会打印出什么呢?
import java.util.*;
public class Name {
private String first, last;
public Name(String first, String last) {
this.first = first;
this.last = last;
}
public boolean equals(Object o) {
if (!(o instanceof Name))
return false;
Name n = (Name)o;
return n.first.equals(first) && n.last.equals(last);
}
public static void main(String[] args) {
Set s = new HashSet();
s.add(new Name("Mickey", "Mouse"));
System.out.println(
s.contains(new Name("Mickey", "Mouse")));
}
}
一个Name实例由一个姓和一个名构成。两个Name实例在通过equals方法进行计算时,如果它们的姓相等且名也相等,则这两个Name实例相等。姓和名是用在String中定义的equals方法来比较的,两个字符串如果以相同的顺序包含相同的若干个字符,那么它们就相等。因此,两个Name实例如果表示相同的名字,那么它们就相等。例如,下面的方法调用将返回true:
new Name("Mickey", "Mouse").equals(new Name("Mickey", "Mouse"))
该程序的main方法创建了两个Name实例,它们都表示Mickey Mouse。该程序将第一个实例放置到了一个散列集合中,然后检查该集合是否包含第二个实例。这两个Name实例是相等的,因此看起来该程序似乎应该打印true。如果你运行它,几乎可以肯定它将打印false。那么这个程序出了什么问题呢?
这里的bug在于Name违反了hashCode约定。这看起来有点奇怪,因为Name连hashCode都没有,但是这确实是问题所在。Name类覆写了equals方法,而hashCode约定要求相等的对象要具有相同的散列码。为了遵守这项约定,无论何时,只要你覆写了equals方法,你就必须同时覆写hashCode方法[EJ Item 8]。
因为Name类没有覆写hashCode方法,所以它从Object那里继承了其hashCode实现。这个实现返回的是基于标识的散列码。换句话说,不同的对象几乎总是产生不相等的散列值,即使它们是相等的也是如此。所以说Name没有遵守hashCode的约定,因此包含Name元素的散列集合的行为是不确定的。
当程序将第一个Name实例放置到散列集合中时,该集合就会在某个散列位置上放置这个实例对应的项。该集合是基于实例的散列值来选择散列位置的,这个散列值是通过实例的hashCode方法计算出来的。
当该程序在检查第二个Name实例是否包含在散列集合中时,它基于第二个实例的散列值来选择要搜索的散列位置。因为第二个实例有别于第一个实例,因此它极有可能产生不同的散列值。如果这两个散列值映射到了不同的位置,那么contains方法将返回false:我们所喜爱的啮齿动物米老鼠就在这个散列集合中,但是该集合却找不到他。
假设两个Name实例映射到了相同的位置,那又会怎样呢?我们所了解的所有的HashSet实现都进行了一种优化,即每一项在存储元素本身之外,还存储了元素的散列值。在搜索某个元素时,这种实现通过遍历集合中的项,去拿存储在每一项中的散列值与我们想要查找的元素的散列值进行比较,从而选取适当的散列位置。只有在两个元素的散列值相等的情况下,这种实现才会认为这两个元素相等。这种优化是有实际意义的,因为比较散列码相对于比较元素来说,其代价要小得多。
对散列集合来说,这项优化并不足以使其能够搜索到正确的位置;两个Name实例必须具有相同的散列值才能让散列集合能够将它们识别为是相等的。该程序偶尔也会打印出true,这是因为被连续创建的两个对象偶尔也会具有相同的标识散列码。一个粗略的实验表明,这种偶然性出现的概率大约是25,000,000分之一。这个实验的结果可能会因所使用的Java实现的不同而有所变化,但是在任何我们所知的JRE上,你基本上是不可能看到该程序打印出true的。
要想订正该程序,只需在Name类中添加一个恰当的hashCode方法即可。尽管任何其返回值仅有姓和名来确定的方法都可以满足hashCode的约定,但是高质量的散列函数应该尝试着对不同的名字返回不同的散列值。下面的方法就能够很好地实现这一点[EJ Item 8]。只要我们把该方法添加到了程序中,那么该程序就可以打印出我们所期望的true:
public int hashCode() {
return 37 * first.hashCode() + last.hashCode();
}
总之,当你覆写equals方法时,一定要记着覆写hashCode方法。更一般地讲,当你在覆写一个方法时,如果它具有一个通用的约定,那么你一定要遵守它。对于大多数在Object中声明的非final的方法,都需要注意这一点[EJ Chapter 3]。不采用这项建议就会导致任意的、不确定的行为。
分享到:
相关推荐
自己改个名字自己改个名字自己改个名字自己改个名字自己改个名字
“如有建议,欢迎来扰”则暗示了开发者愿意接受用户的反馈和建议,以便对软件进行改进。 根据标签“复姓 名字 生成 工具”,我们可以推断这个软件的主要功能包括: 1. **复姓库**:提供了82个复姓,用户可以从中...
在IT行业中,"按照名字找地址按照地址找名字工程"可能指的是一个系统或者应用,它主要功能是通过名称查询对应的地址信息,同时也能根据地址反查相关的名字。这样的系统常见于地址簿、联系人管理、物流配送、地理信息...
在课程的第三部分,学生被邀请介绍自己名字的故事,理解家长在给孩子取名时的深意,这有助于增强学生对自己名字的认同感,同时锻炼他们的口头表达技巧和团队协作能力。通过小组交流和全班分享,课堂氛围活跃,促进...
在CF战队名字大全5个字中,我们可以看到不同的国家和地区都有其特色的战队名字。例如,丹麦战队SK-Gaming的名字来源于丹麦的文化和历史,澳大利亚战队function zero的名字则体现了澳大利亚的 jeune esprit。韩国战队...
维吾尔人名字翻译工具
这些名字虽然基础相同但拼写略有差异,反映了不同文化和地区的习惯。 - **昵称**:`addie`, `abby`, `albie`。这类简化的名字通常作为昵称使用,给人以亲切感。 - **非传统名字**:`akim`, `aksel`。这些名字在某些...
这个一个很有创意的名字变换,但是很傻很天真.希望下载后不要骂人就行了。
十分好用的一款抽名字器,功能齐全够用,程序内自带教程,只要配合名单使用即可!
在信息化时代,这样的工具在各种场景中都有其独特用途,例如在文学创作、角色设定、数据分析、用户测试等场景下,能够快速生成多样化的姓名组合,提高工作效率。 在生成姓名的过程中,工具通常会依据一定的规则和...
4. 用户界面(UI)设计:程序有一个简单的用户界面,至少包含一个按钮和一个显示区域,展示生成的名字。 5. 事件驱动编程:通过监听用户的点击事件,触发名字生成的逻辑。 6. 可能的优化:为了提供更好的用户体验...
幼儿园大班英语公开课教案《你叫什么名字》润新教育.txt
标题“sql注入_表名、列名字典”提示我们,这个话题将涉及到如何通过SQL注入来获取数据库中的表名和列名。 在描述中,“union select * from 表名字典 union select 列名字典 from 表名”是一个典型的SQL注入攻击...
每个控件都有相应的属性(如Text、ForeColor、Font)和事件(如Click事件)。 4. **事件处理** 在C#中,事件处理是程序与用户交互的关键。比如,当用户点击按钮时,会触发按钮的Click事件。开发者会为这些事件编写...
这款游戏的独特之处在于它将个人的名字融入游戏流程,使得每个玩家都有独一无二的游戏经历。 在这款游戏中,首先,玩家会被要求输入自己的名字。这个名字将会成为游戏中的主角,出现在各种场景和对话中,增强了玩家...
然而,游戏本身对昵称的长度有限制,这使得玩家难以实现某些长名字的愿望。此时,《王者荣耀长名字助手》便发挥了它的作用。 该应用的核心功能是生成特殊字符,这些字符在游戏系统中可能被视为单个字符,但视觉上看...
1. 猫和老鼠都有名字和体重两种属性,猫有抓老鼠的方法,对应的老鼠则有逃跑的方法。 2. 猫抓住了老鼠或者老鼠逃跑了,对于这两种情况,我们用体重、技能和速度来区分,若猫的体重、技能和速度大于或等于老鼠的体重...
精准宝宝取名软件名字分析精准宝宝取名软件名字分析精准宝宝取名软件名字分析精准宝宝取名软件名字分析精准宝宝取名软件名字分析精准宝宝取名软件名字分析精准宝宝取名软件名字分析精准宝宝取名软件名字分析精准宝宝...
车站站名字典在信息化管理系统中用于车站信息管理系统,车站站名字典在信息化管理系统中用于车站信息管理系统
在完成CCNA的学习和考试后,有时可能会出现填写个人信息时不小心出错的情况,比如在申请过程中输入的名字不正确。本文将详细解释如何处理"ccna case申请修改名字"的问题。 首先,当你发现自己的CCNA认证申请中的...