AnthonyZero's Bolg

Java基础:Hashcode与equals

equals() 的作用

equals() 的作用是 用来判断两个对象是否相等。

equals() 定义在JDK的Object.java中。通过判断两个对象的地址是否相等(即,是否是同一个对象)来区分它们是否相等。Object源码如下

public boolean equals(Object obj) {
    return (this == obj);
}

既然Object.java中定义了equals()方法,这就意味着所有的Java类都实现了equals()方法,所有的类都可以通过equals()去比较两个对象是否相等。但是,使用默认的“equals()”方法,等价于“==”用法。因此,我们通常会重写equals()方法:若两个对象的内容相等,则equals()方法返回true;否则,返回fasle。

下面根据“类是否覆盖equals()方法”,将它分为2类。
1) 若某个类没有覆盖equals()方法,当它通过equals()比较两个对象时,实际上是比较两个对象是不是同一个对象。这时,等价于通过“==”去比较这两个对象。
2) 我们可以覆盖类的equals()方法,来让equals()通过其它方式比较两个对象是否相等。通常的做法是:若两个对象的内容相等,则equals()方法返回true;否则,返回fasle。(String、Double、Integer、Math…等等这些类都是重写了equals()方法的,从而进行的是内容的比较

equals() 与 == 的区别是什么?

== : 它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不试同一个对象

equals() : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况(前面第1部分已详细介绍过):
情况1,类没有覆盖equals()方法。则通过equals()比较该类的两个对象时,等价于通过“==”比较这两个对象。
情况2,类覆盖了equals()方法。一般,我们都覆盖equals()方法来两个对象的内容相等;若它们的内容相等,则返回true(即,认为这两个对象相等)

hashCode() 的作用

hashCode()的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。hashCode()方法是一个native方法,它的返回值默认与System.identityHashCode(object)一致。这个值是对象头的一部分二进制位组成的数字,这个数字具有一定的标识对象的意义所在,但绝不等价于地址.
这个哈希码的作用是确定该对象在哈希表中的索引位置。Java中的任何类都包含有hashCode() 函数

首先我们要区别hashCode和内存地址,不要把他们当成相同的东西,hashCode仅仅是一种标识,我们尽量让他具有唯一性,但是并不保证这一点,标识可以帮助我们迅速找到他,提高查找的效率,仅此而已

public class Object {
·······

    /**
     * 返回该对象的哈希码值。
     * 支持此方法是为了提高哈希表(例如 java.util.Hashtable 提供的哈希表)的性能
     * {@link java.util.HashMap}.
     * <p>
     * hashCode 的常规协定是:
     * <ul>
     * <li>在 Java 应用程序执行期间,在对同一对象多次调用 hashCode 方法时,
     *     必须一致地返回相同的整数,前提是将对象进行 equals 比较时所用的信息没有被修改。
     *     从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。
     * <li>如果根据 equals(Object) 方法,两个对象是相等的,
     *     那么对这两个对象中的每个对象调用 hashCode 方法都必须生成相同的整数结果。
     * <li>如果根据 equals(java.lang.Object) 方法,两个对象不相等,
     *     那么对这两个对象中的任一对象上调用 hashCode 方法不 要求一定生成不同的整数结果。
     *     但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能。
     * </ul>
     * <p>
     * 实际上,由 Object 类定义的 hashCode 方法确实会针对不同的对象返回不同的整数。
     * (这一般是通过将该对象的内部地址转换成一个整数来实现的,
     * 但是 JavaTM 编程语言不需要这种实现技巧。)
     *
     * @return  此对象的一个哈希码值。
     * @see     java.lang.Object#equals(java.lang.Object)
     * @see     java.lang.System#identityHashCode
     */
    public native int hashCode();
·······
}

总的来说,hashCode()在哈希表中起作用,如HashSet、HashMap等。
当我们向哈希表(如HashSet、HashMap等)中添加对象object时,首先调用hashCode()方法计算object的哈希码,通过哈希码可以直接定位object在哈希表中的位置(一般是哈希码对哈希表大小取余)。如果该位置没有对象,可以直接将object插入该位置;如果该位置有对象(可能有多个,通过链表实现),则调用equals()方法比较这些对象与object是否相等,如果相等,则不需要保存object;如果不相等,则将该对象加入到链表中。
这也就解释了为什么equals()相等,则hashCode()必须相等。如果两个对象equals()相等,则它们在哈希表(如HashSet、HashMap等)中只应该出现一次;如果hashCode()不相等,那么它们会被散列到哈希表的不同位置,哈希表中出现了不止一次

  • hashCode主要用于提升查询效率,来确定在散列结构中对象的存储地址;
    重写equals()必须重写hashCode(),二者参与计算的自身属性字段应该相同
  • hash类型的存储结构,添加元素重复性校验的标准就是先取hashCode值,后判断equals();
    equals()相等的两个对象,hashcode()一定相等;反过来:hashcode()不等,一定能推出equals()也不等;
    hashcode()相等,equals()可能相等,也可能不等

hashCode() 和 equals() 的关系

第一种 不会创建“类对应的散列表”

这里所说的“不会创建类对应的散列表”是说:我们不会在HashSet, Hashtable, HashMap等等这些本质是散列表的数据结构中,用到该类。例如,不会创建该类的HashSet集合

在这种情况下,该类的“hashCode() 和 equals() ”没有半毛钱关系的!
这种情况下,equals() 用来比较该类的两个对象是否相等。而hashCode() 则根本没有任何作用,所以,不用理会hashCode()

第二种 会创建“类对应的散列表”

这里所说的“会创建类对应的散列表”是说:我们会在HashSet, Hashtable, HashMap等等这些本质是散列表的数据结构中,用到该类。例如,会创建该类的HashSet集合。

在这种情况下,该类的“hashCode() 和 equals() ”是有关系的:
1)、如果两个对象相等,那么它们的hashCode()值一定相同。
这里的相等是指,通过equals()比较两个对象时返回true。
2)、如果两个对象hashCode()相等,它们并不一定相等。
因为在散列表中,hashCode()相等,即两个键值对的哈希值相等。然而哈希值相等,并不一定能得出键值对相等。补充说一句:“两个不同的键值对,哈希值相等”,这就是哈希冲突。
此外,在这种情况下。若要判断两个对象是否相等,除了要覆盖equals()之外,也要覆盖hashCode()函数。否则,equals()无效.
例如,创建Person类的HashSet集合,必须同时覆盖Person类的equals() 和 hashCode()方法。
如果单单只是覆盖equals()方法。我们会发现,equals()方法没有达到我们想要的效果。