覆写equals方法必须覆写hashCode方法,是JDK API上反复说明的,不过为什么要这样做呢?这两个方法之间有什么关系呢?

void test() {
// Person类的实例作为Map的key
Map<Person, Object> map = new HashMap<Person, Object>();
map.put(new Person("张三"), new Object()); // Person类的实例作为List的元素
List<Person> list = newArrayList<Person>()
list.add(new Person("张三")); // 列表中是否包含
boolean b1 = list.contains(new Person("张三"));
// Map中是否包含
boolean b2 = map.containsKey(new Person("张三"));
System.out.println(b1 + "|" + b2);
}

Person类:

public class Person {
private String name;
private String age; public Person(String name) {
setName(name);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
} @Override
public boolean equals(Object o) {
/*
* 使用getClass是为了防止子类被判断为父类的情况
* 例如:Person中覆写equals方法 {return name.equals(p.getName);}
* 子类Employee继承Person,此时子类中的euqals方法super.equlas(p);
* 此时如果使用instanceof关键字,因为Employee是Person的子类,所以返回true
* 进一步调用equals返回的是true,所以会将Person和Employee认为是同一人,显然不合理
*/
if(o != null && o.getClass() == this.getClass()) {
Person p = (Person) o;
if(p.getName() == null && this.name == null)
return false;
return this.name.equals(p.getName());
}
return false;
}
}

  我们先来看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方法即可,代码如下:

public int hashCode() {
//其中HashCodeBuilder是org.apache.commons.lang.builder包下的一个哈希码生成工具,
//使用起来非常方便,诸位可以直接在项目中集成。
return new HashCodeBuilder().append(name).toHashCode();
}

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

覆写equals方法为什么需要覆写hashCode方法的更多相关文章

  1. 第九条:覆盖equals方法时总要覆盖hashCode方法

    Object类的hashCode方法: public native int hashCode();   是一个本地方法. 其中这个方法的主要注释如下: Whenever it is invoked o ...

  2. 半夜思考, 为什么建议重写 equals() 方法时, 也要重写 hashCode() 方法

    我说的半夜, 并不是真正的半夜, 指的是在我一个人的时候, 我会去思考一些奇怪的问题. 要理解 hashCode() 需要理解下面三个点: hash契约 哈希冲突 哈希可变 第一点: hash 契约指 ...

  3. JAVA中重写equals()方法的同时要重写hashcode()方法

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

  4. 【Java实战】源码解析为什么覆盖equals方法时总要覆盖hashCode方法

    1.背景知识 本文代码基于jdk1.8分析,<Java编程思想>中有如下描述: 另外再看下Object.java对hashCode()方法的说明: /** * Returns a hash ...

  5. 重写equals方法,也应该重写hashcode方法,反之亦然

    yls 2019年11月07日 一方面 hashcode原则:两个对象equals相等,hashcode值一定相等 默认的hashcode是Object类通过对象的内存地址得到的 若重写equals而 ...

  6. 为什么在重写 equals 方法的时候需要重写 hashCode 方法?

    因为有强制的规范指定需要同时重写 hashcode 与 equal 是方法,许多容器类, 如 HashMap.HashSet 都依赖于 hashcode 与 equals 的规定.

  7. Effective Java 第三版——11. 重写equals方法时同时也要重写hashcode方法

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

  8. 浅谈Java中的hashcode方法以及equals方法

    哈希表这个数据结构想必大多数人都不陌生,而且在很多地方都会利用到hash表来提高查找效率.在Java的Object类中有一个方法: public native int hashCode(); 根据这个 ...

  9. 为什么重写equals时必须重写hashCode方法?

    原文地址:http://www.cnblogs.com/shenliang123/archive/2012/04/16/2452206.html 首先我们先来看下String类的源码:可以发现Stri ...

  10. Java中==、equals、hashcode的区别与重写equals以及hashcode方法实例(转)

    Java中==.equals.hashcode的区别与重写equals以及hashcode方法实例  原文地址:http://www.cnblogs.com/luankun0214/p/4421770 ...

随机推荐

  1. java.io.IOException No space left on device

    磁盘空间不足 1.df -k,发现程序所在的工作目录/data,居然到了100%. 1604050 free allocated Kb 535144219 used allocated Kb 100 ...

  2. Tensorflow高效读取数据

    关于Tensorflow读取数据,官网给出了三种方法: 供给数据(Feeding): 在TensorFlow程序运行的每一步, 让Python代码来供给数据. 从文件读取数据: 在TensorFlow ...

  3. PL/SQL 查询的数据出现乱码

    解决方法: 1.首先在查询出Oracle数据库的字符集. select userenv('language') from dual; 2.新建系统变量 NLS_LANG,变量值为第一步查询出来的字符集 ...

  4. 单元测试工具Junit浅谈

    什么是单元测试?   写了一个类和一些方法,给别人用,会不会有bug?那就测一下这些方法吧 怎么测?   用main方法测?不能一起运行,需要人为观察输出是否正确,测试效率低 单元测试能带来什么好处? ...

  5. python并发编程之线程

    操作系统线程理论 参考资料:http://www.cnblogs.com/Eva-J/articles/8306047.html 线程和python 理论知识 全局解释器锁GIL Python代码的执 ...

  6. Appium学习——安装Android SDK

    .下载Android SDK 下载地址:http://tools.android-studio.org/index.php/sdk 百度搜索Android SDK也可以. 下载之后,Android S ...

  7. SpringBoot整合Rabbitmq设置消息请求头

    String str = "{\"origin\":\"BBC\",\"origin_coupon_id\":51,\" ...

  8. January 12th, 2018 Week 02nd Friday

    Nothing behind me, everything ahead of me, as is ever so on the road. 我的身后空空荡荡,整个世界都在前方,这就是在路上. That ...

  9. visual studio code前端插件及常用快捷键【转】

    通用插件 HTML Snippets 超级实用且初级的 H5代码片段以及提示 HTML CSS Support 让 html 标签上写class 智能提示当前项目所支持的样式新版已经支持scss文件检 ...

  10. Properties集合概述与存和取

    Properties集合:(Map集合下的Hashtable集合下的一个子类) 特点: 1.集合中的键和值都是字符串 2.集合中的数据可以保存到流中,或者从流中获取 该集合通常用于操作以键值对形式存在 ...