覆写equals方法必须覆写hashCode方法,这条规则基本上每个Javaer都知道,这也是JDK API上反复说明的,不过为什么要这样做呢?这两个方法之间有什么关系呢?本建议就来解释该问题,我们先来看如下代码:

 public static void main(String[] args) {
// Person类的实例作为Map的key
Map<Person, Object> map = new HashMap<Person, Object>() {
{
put(new Person("张三"), new Object());
}
};
// Person类的实例作为List的元素
List<Person> list = new ArrayList<Person>() {
{
add(new Person("张三"));
}
};
// 列表中是否包含
boolean b1 = list.contains(new Person("张三"));
// Map中是否包含
boolean b2 = map.containsKey(new Person("张三"));
}

代码中的Person类与上一建议相同(http://www.cnblogs.com/DreamDrive/p/5431603.html),euqals方法完美无缺。在这段代码中,我们在声明时直接调用方法赋值,这其实也是一个内部匿名类的操作(下一个建议会详细说明)。现在的问题是b1和b2这两个boolean值是否都为true?

我们先来看b1,Person类的equals覆写了,不再判断两个地址是否相等,而是根据人员的姓名来判断两个对象是否相等,所以不管我们的new Person(“张三”)产生了多少个对象,它们都是相等的。把“张三”对象放入List中,再检查List中是否包含,那结果肯定是true了。

接着来看b2,我们把张三这个对象作为了Map的键(Key),放进去的对象是张三,检查的对象还是张三,那应该和List的结果相同了,但是很遗憾,结果是false。原因何在呢?

原因就是HashMap的底层处理机制是以数组的方式保存Map条目(Map Entry)的,这其中的关键是这个数组下标的处理机制:依据传入元素hashCode方法的返回值决定其数组的下标,如果该数组位置上已经有了Map条目,且与传入的键值相等则不处理,若不相等则覆盖;如果数组位置没有条目,则插入,并加入到Map条目的链表中。同理,检查键是否存在也是根据哈希码确定位置,然后遍历查找键值的。

接着深入探讨,那对象元素的hashCode方法返回的是什么值呢?它是一个对象的哈希码,是由Object类的本地方法生成的,确保每个对象有一个哈希码(这也是哈希算法的基本要求:任意输入k,通过一定算法f(k),将其转换为非可逆的输出,对于两个输入k1和k2,要求若k1=k2,则必须f(k1)=f(k2),但也允许k1≠k2,f(k1)=f(k2)的情况存在)。

那回到我们的例子上,由于我们没有重写hashCode方法,两个张三对象的hashCode方法返回值(也就是哈希码)肯定是不相同的了,在HashMap的数组中也就找不到对应的Map条目了,于是就返回了false。

问题清楚了,修改也非常简单,重写一下hashCode方法即可,代码如下:

class Person {
/*其他代码相同,不再赘述*/
@Override
public int hashCode() {
return new HashCodeBuilder().append(name).toHashCode();
}
}

其中HashCodeBuilder是org.apache.commons.lang.builder包下的一个哈希码生成工具,使用起来非常方便,诸位可以直接在项目中集成。(为什么不直接写hashCode方法?因为哈希码的生成有很多种算法,自己写麻烦,事儿又多,所以采用拿来主义是最好的方法。)

[改善Java代码]覆写equals方法必须覆写hashCode方法的更多相关文章

  1. 为什么覆写equals必须要覆写hashCode?

    ============================================= 原文链接: 为什么覆写equals必须要覆写hashCode? 转载请注明出处! ============= ...

  2. JAVA中重写equals()方法为什么要重写hashcode()方法?

    object对象中的 public boolean equals(Object obj),对于任何非空引用值 x 和 y,当且仅当 x 和 y 引用同一个对象时,此方法才返回 true:注意:当此方法 ...

  3. 一文搞懂--Java中重写equals方法为什么要重写hashcode方法?

    Java中重写equals方法为什么要重写hashcode方法? 直接看下面的例子: 首先我们只重写equals()方法 public class Test { public static void ...

  4. 为什么重写equals()方法就必须重写hashCode()方法

    hashCode()和equals()保持一致,如果equals方法返回true,那么两个对象的hasCode()返回值必须一样.如果equals方法返回false,hashcode可以不一样,但是这 ...

  5. [改善Java代码]覆写equals方法时不要识别不出自己

    建议45: 覆写equals方法时不要识别不出自己 我们在写一个JavaBean时,经常会覆写equals方法,其目的是根据业务规则判断两个对象是否相等,比如我们写一个Person类,然后根据姓名判断 ...

  6. [改善Java代码]equals应该考虑null值的情景

    建议46: equals应该考虑null值情景 继续上一建议的问题,我们解决了覆写equals的自反性问题,是不是就很完美了呢?再把main方法重构一下: public class Client { ...

  7. [改善Java代码]在equals中使用getClass进行类型判断

    建议47: 在equals中使用getClass进行类型判断 本节我们继续讨论覆写equals的问题.这次我们编写一个员工Employee类继承Person类,这很正常,员工也是人嘛,而且在JEE中J ...

  8. [改善Java代码]用枚举实现工厂方法模式更简洁

    工厂方法模式(Factory Method Patter)是"创建对象的接口",让子类决定实例化哪一个类,并使一个类的实例化延迟到其子类.工厂方法模式在我们的开发工作中,经常会用到 ...

  9. 改善JAVA代码01:考虑静态工厂方法代替构造器

    前言 系列文章:[传送门]   每次开始新的一本书,我都会很开心.新书新心情. 正文 静态工厂方法代替构造器 说起这个,好多可以念叨的.做了一年多的项目,慢慢也有感触. 说起构造器 大家很明白,构造器 ...

随机推荐

  1. JAVA中的常见面试题1

    1.线程同步的方法的使用. sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常. wait():使一个线程处于等待状态,并且 ...

  2. POJ 1679 The Unique MST (最小生成树)

    The Unique MST 题目链接: http://acm.hust.edu.cn/vjudge/contest/124434#problem/J Description Given a conn ...

  3. mysql一些写常用命令

    参见pcttcnc2007博客腾飞 1.mysql的status信息命令: mysql> show global status; 2.可以列出mysql服务器运行各种状态值,另外,查询mysql ...

  4. 分析恶意驱动(进程启动apc注入dll)

    一.前言  用IDA也有好些时间了,以前就只会用F5功能玩无壳无保护的裸驱动,感觉太坑了,这两天就开始看网上大牛的逆向. 今天记录一下sudami曾经逆向过的fuck.sys.第一遍自己走的时候漏掉了 ...

  5. C#编程简短总结

    封装 field一般为private,定义的时候可以不赋值.不赋值的时候一般被构造函数初始化赋值,其值用来保存类实例的数据,可以被内部方法使用作为计算的数据来源.当需要继承类继承本类的时候,field ...

  6. 使用 EPPlus,NPOI,操作EXCEL

    NPOI,       读取xls文件(Excel2003及之前的版本)   (NPOI.dll+Ionic.Zip.dll)     http://npoi.codeplex.com/ EPPlus ...

  7. INV(库存管理)

    物料 PROCEDURE update_item(p_init_msg_list IN VARCHAR2 DEFAULT fnd_api.g_false, x_return_status OUT NO ...

  8. 一个序列是否可能是push序列的pop序列

    输入:两个序列A.B,A作栈的push序列,判断B是否可能是A的pop序列. #include<iostream> #include<stack> using namespac ...

  9. Spark 1.3.0 单机安装

    一.试验环境: CentOS6.6 最小化安装:主机名spark-test,IP:10.10.10.26 OpenStack虚拟云主机. 注:安装流程:进入linux->安装JDK->安装 ...

  10. 【SQL】SQL2012 导入导出报错,未在计算机上注册...

    导出时报错: 如图: 解决方法:下载插件: 下载地址:http://download.microsoft.com/download/7/0/3/703ffbcb-dc0c-4e19-b0da-1463 ...