覆写equals方法为什么需要覆写hashCode方法

  覆写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方法?因为哈希码的生成有很多种算法,自己写麻烦,事儿又多,所以采用拿来主义是最好的方法。)

时间: 2024-11-11 06:04:46

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

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

我说的半夜, 并不是真正的半夜, 指的是在我一个人的时候, 我会去思考一些奇怪的问题. 这次思考的如题所示, 为什么重写 equals() 方法时, 强烈建议重写 hashCode() 方法, 这个答案, 应该大多数人都知道, 是为了减少 equals() 方法的调用, 只有当两个对象的 hashCode 相等时, 才会去调用 equals()方法去判断两个对象是否相等, 减少了equals()的调用, 提高了效率 . 话是这么说,  的确, 可以减少很多次的 equals()方法的调用, 但是

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

1.背景知识 本文代码基于jdk1.8分析,<Java编程思想>中有如下描述: 另外再看下Object.java对hashCode()方法的说明: /** * Returns a hash code value for the object. This method is * supported for the benefit of hash tables such as those provided by * {@link java.util.HashMap}. * <p> *

重写equals方法,就应该重写Hashcode方法。反过来呢?

1.如果equals两个对象相同,那么hashcode也必须相同. 2.hashcode相同,两个对象equals不一定相同.(可能发生散列碰撞) 为了提高效率才实现hashcode方法,先进行散列,如果不同,就没必要比较equals了. 因为做散列的速度远远快于equals.. 减少了equals的比较次数,提高效率.集合中就是这么使用的.例如HashMap 散列碰撞概率 http://www.freezhongzi.info/?p=100

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

object对象中的 public boolean equals(Object obj),对于任何非空引用值 x 和 y,当且仅当 x 和 y 引用同一个对象时,此方法才返回 true:注意:当此方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码.如下:(1)当obj1.equals(obj2)为true时,obj1.hashCode() == obj2.hashCode()必须为true (2)当obj1.ha

【java基础】重写equals()方法的同时要重写hashCode()方法

1. 为什么要重写equals方法? 因为Object的equal方法默认是两个对象的引用的比较,意思就是指向同一内存,地址则相等,否则不相等:如果你现在需要利用对象里面字段的值来判断是否相等,则重写equals方法. 2. 为什么重写hashCode方法? 一般的地方不需要重写hashCode,只有当类需要放在HashTable.HashMap.HashSet等等hash结构的集合时才会重写hashCode,那么为什么要重载hashCode呢?就HashMap来说,好比HashMap就是一个大

详解equals()方法和hashCode()方法

前言 Java的基类Object提供了一些方法,其中equals()方法用于判断两个对象是否相等,hashCode()方法用于计算对象的哈希码.equals()和hashCode()都不是final方法,都可以被重写(overwrite). 本文介绍了2种方法在使用和重写时,一些需要注意的问题. 一.equal()方法 Object类中equals()方法实现如下: public boolean equals(Object obj) { return (this == obj); } 通过该实现

【转】Java中==、equals、hashcode的区别与重写equals以及hashcode方法实例

原文地址:http://www.cnblogs.com/luankun0214/p/4421770.html 感谢网友的分享,记录下来只为学习. 1.重写equals方法实例   部分代码参考http://blog.csdn.net/wangloveall/article/details/7899948 重写equals方法的目的是判断两个对象的内容(内容可以有很多,比如同时比较姓名和年龄,同时相同的才是用一个对象)是否相同 如果不重写equals,那么比较的将是对象的引用是否指向同一块内存地址

重写equals()方法时,需要同时重写hashCode()方法

package com.wangzhu.map; import java.util.HashMap; /** * hashCode方法的主要作用是为了配合基于散列的集合一起正常运行,<br/> * 这样的散列集合包括HashSet.HashMap以及HashTable.<br/> * 能否可以直接根据hashCode值判断两个对象是否相等呢?<br/> * 答案:肯定是不可以的,因为不同的对象可能会生成相同的hashCode值.<br/> * 虽然不能根据h

判断Set里的元素是否重复、==、equals、hashCode方法研究-代码演示

被测试类,没有重写hasCode()和equals()方法: package niukewang; import java.util.Objects; public class setClass { String a; String b; public setClass(String a, String b) { this.a=a; this.b=b; } } 测试类: package niukewang; import java.util.HashSet; import java.util.S

为什么重写equals时必须重写hashCode方法?(转发+整理)

为什么重写equals时必须重写hashCode方法? 原文地址:http://www.cnblogs.com/shenliang123/archive/2012/04/16/2452206.html 首先我们先来看下String类的源码:可以发现String是重写了Object类的equals方法的,并且也重写了hashcode方法 public boolean equals(Object anObject) { if (this == anObject) { return true; } i