重写equals和hashCode

一、equals和hashcode方法都来自Object对象。

    API文档中HashCode和equals方法定义java

public int hashCode()程序员

返回该对象的哈希码值。支持此方法是为了提升哈希表性能。hashCode的常规协定是编程

  • 在Java应用程序执行期间,在对同一对象屡次调用hashCode方法时,必须一致地返回相同的整数,前提是将对象进行equals比较时信息没有被修改。
  • 若是根据equals()方法,两个对象是相等的,那么对这两个对象中的每一个对象调用hashCode方法都必须生成相同的整数结果。
  • 若是根据equals()方法,两个对象不相等,那么对这两个对象中的任一对象上调用hashCode方法不要求必定生成不一样的整数结果,可是程序员应该意识到,为不想等的对象生成不一样的整数结果能够提升哈希表的性能。
  • 实际上,由Object类定义的hashCode方法确实对针对不一样的对象返回不一样的整数。这通常是经过将该对象的内部地址转换成一个整数来实现的,可是Java编程语言不须要这种实现技巧。

public boolean equals()数组

指示其余某个对象是否与此对象“相等”。equals 方法在非空对象引用上实现相等关系:编程语言

  • 自反性:对于任何非空引用值 x,x.equals(x) 都应返回 true。
  • 对称性:对于任何非空引用值 x 和 y,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 才应返回 true。
  • 传递性:对于任何非空引用值 x、y 和 z,若是 x.equals(y) 返回 true,而且 y.equals(z) 返回 true,那么 x.equals(z) 应返回 true。
  • 一致性:对于任何非空引用值 x 和 y,屡次调用 x.equals(y) 始终返回 true 或始终返回 false,前提是对象上 equals 比较中所用的信息没有被修改。
    对于任何非空引用值 x,x.equals(null) 都应返回 false。
  • Object 类的 equals 方法实现对象上差异可能性最大的相等关系;即,对于任何非空引用值 x 和 y,当且仅当 x 和 y 引用同一个对象时,此方法才返回 true(x == y 具备值 true)。
  • 注意:当此方法被重写时,一般有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具备相等的哈希码

二、equals重写场景

    当一个类有本身特有的“逻辑相等”概念(不一样于对象身份的概念)时,须要重写equals方法性能

三、equals和hashCode方法设计

    3.一、equals方法设计

        【1】.使用instanceof操做符检查“实参是否为正确的类型”。优化

        【2】.对于类中的每个“关键域”,检查实参中的域与当前对象中对应的阈值。this

            【2.1】对于非float和double类型的原语类型域,使用==比较spa

            【2.2】对于对象引用域,递归调用equals方法设计

            【2.3】对于float域,使用Float.floatToIntBits(afloat)转化为int,再使用==比较。

            【2.4】对于数组域,调用Arrays.equals方法.

    3.二、hashCode方法设计

        【1】.把某个非零常数值,保存在int变量result中
        【2】.对于对象中每个关键域f(指equals方法中考虑的每个域):
             【2.1】boolean型,计算(f?0:1)
             【2.2】byte,char,short型,计算(int)
             【2.3】long型,计算(int)(f ^(f >>>32))
             【2.4】float型,计算Float.floatToIntBits(afloat)
             【2.5】double型,计算Double.doubleToLongBits(abouble)获得一个long,再执行[2.3]
             【2.6】对象引用,递归调用它的hashCode方法
             【2.7】数组域,对其中每一个元素调用它的hashCode方法
        【3】.将上面计算获得的散列码保存到int变量c中,而后执行result=37*result +c;
        【4】.返回result

四、使用示例

    在Object类中定义的hashCode方法代码以下:

public native int hashCode();

这说明hashCode()本地机器相关的方法。jre6中String实现hashCode()的代码以下:

/**
     * Returns a hash code for this string. The hash code for a
     * {@code String} object is computed as
     * <blockquote><pre>
     * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
     * </pre></blockquote>
     * using {@code int} arithmetic, where {@code s[i]} is the
     * <i>i</i>th character of the string, {@code n} is the length of
     * the string, and {@code ^} indicates exponentiation.
     * (The hash value of the empty string is zero.)
     *
     * @return  a hash code value for this object.
     */
    public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }

    String实现hashCode()方法中利用了公式s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]。对这公式的解读:

s[i]是string的第i个字符,n是String的长度。31为啥呢?之因此选择31,是由于它是个奇素数,若是乘数是偶数,而且乘法溢出的话,信息就会丢失,由于与2相乘等价于移位运算。使用素数的好处并非很明显,可是习惯上都使用素数来计算散列结果。31有个很好的特性,就是用移位和减法来代替乘法,能够获得更好的性能:31*i==(i<<5)-i。如今的VM能够自动完成这种优化。

五、总结

  • 若是两个对象调用equals(),返回结果为true,那么这两个对象必定有相同的hashCode
  • 若是两个对象调用equals(),返回结果为false,这两个对象能够有相同的hashCode。可是这样的结果会形成散列表退化成链表,性能下降。
  • 若是两个对象的hashCode同样,那么这两个对象必定相等。
  • 若是对象重写了equals()方法,请程序员务必重写hashCode(),遵循hashCode协定,提升系统能。
相关文章
相关标签/搜索