equals()方法的重写问题

equals()方法的重写问题

  • 何时需要重写equals()

每一个java类都继承自object类,而equals()是Object类中提供的方法之一。在Object类中,equals()的源码如下:

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

由此可以看出,Object类中的equals()实际上比较的是两个引用指向的是否是同一个对象,即比较实例是否相等,此时,equals()与“==”的用法并无区别。然而,当我们使用equals()对两个对象进行比较时,往往是希望比较两个对象是否逻辑相等而非是否指向同一对象,所以在此情况下为了实现预期功能我们需要对equals()进行重写。而且这样做也使得这个类的实例可以被用做映射表(map)的键,或者集合(set)的元素,并使映射表或者集合表现出预期的行为。

  • 怎么样重写equals()

重写equals()需要遵守其通用约定,来自java.lang.Object的规范:
equals方法实现了等价关系(equivalence relation):
1. 自反性:对于任意的引用值x,x.equals(x)一定为true。
2. 对称性:对于任意的引用值x 和 y,当x.equals(y)返回true时,
  y.equals(x)也一定返回true。
3. 传递性:对于任意的引用值x、y和z,如果x.equals(y)返回true,
  并且y.equals(z)也返回true,那么x.equals(z)也一定返回true。
4. 一致性:对于任意的引用值x 和 y,如果用于equals比较的对象信息没有被修
  改,多次调用x.equals(y)要么一致地返回true,要么一致地返回false。
5. 非空性:对于任意的非空引用值x,x.equals(null)一定返回false。

重写equals方法的诀窍:

  1. 使用==操作符检查“实参是否为指向对象的一个引用”。
  2. 使用instanceof操作符检查“实参是否为正确的类型”。
  3. 把实参转换到正确的类型。
  4. 对于该类中每一个“关键”域,检查实参中的域与当前对象中对应的域值是否匹配。对于既不是float也不是double类型的基本类型的域,可以使用==操作符进行比较;对于对象引用类型的域,可以递归地调用所引用的对象的equals方法;对于float类型的域,先使Float.floatToIntBits转换成int类型的值,然后使用==操作符比较int类型的值;对于double类型的域,先使用Double.doubleToLongBits转换成long类型的值,然后使用==操作符比较long类型的值。
  5. 当你编写完成了equals方法之后,应该问自己三个问题:它是否是对称的、传递的、一致的?(其他两个特性通常会自行满足)如果答案是否定的,那么请找到 这些特性未能满足的原因,再修改equals方法的代码。

  • 当改写equals()的时候,总是要改写hashCode()

根据一个类的equals方法(改写后),两个截然不同的实例有可能在逻辑上是相等的,但是,根据Object.hashCode()方法,它们仅仅是两个对象。因此,违反了“相等的对象必须具有相等的散列码”。
如果你重载了equals(),比如说是基于对象的内容实现的,而保留hashCode()的实现不变,那么很可能某两个对象明明是“相等”,而hashCode()却不一样。
这样,当你用其中的一个作为键保存到hashMap、hasoTable或hashSet中,再以“相等的”找另一个作为键值去查找他们的时候,则根本找不到。

然而实际上,equals() 方法和 hashcode() 方法没有必然关系。用途不同, equals() 用来比较两个对象是否相等,在大多数 JDK 的集合类中判断唯一性的时候使用的都是 equals() 方法。而 hashcode() 方法用来计算对象的 Hash 值,基于 Hash 算法存放数据的集合会用到,比如 HashMap、HashSet。equals() 方法很好理解,区别于直接比较对象内存地址的 == ,它被设计为用来比较对象内容语义上的相等。而要理解 hashcode 方法,首先你要知道什么是散列算法,了解一下 HashMap 底下的存储结构和存放读取数据的过程(对 key 调用 hashcode() 得散列值,找到该散列值对应的桶,往桶里放 value)。你可以认为 hashcode() 是为了给对象分类用的。

  • 示例
package com.unit;
import java.util.Arrays;

public class Unit {
    private short ashort;
    private char achar;
    private byte abyte;
    private boolean abool;
    private long along;
    private float afloat;
    private double adouble;
    private Unit aObject;
    private int[] ints;
    private Unit[] units;

    public boolean equals(Object o) {
       if (!(o instanceof Unit))
           return false;
       Unit unit = (Unit) o;
       return unit.ashort == ashort
              && unit.achar == achar
              && unit.abyte == abyte
              && unit.abool == abool
              && unit.along == along
              && Float.floatToIntBits(unit.afloat) == Float
                     .floatToIntBits(afloat)
              && Double.doubleToLongBits(unit.adouble) == Double
                     .doubleToLongBits(adouble)
              && unit.aObject.equals(aObject)
&& equalsInts(unit.ints)
              && equalsUnits(unit.units);
    }

    private boolean equalsInts(int[] aints) {
       return Arrays.equals(ints, aints);
    }

    private boolean equalsUnits(Unit[] aUnits) {
       return Arrays.equals(units, aUnits);
    }

    public int hashCode() {
       int result = 17;
       result = 37 * result + (int) ashort;
       result = 37 * result + (int) achar;
       result = 37 * result + (int) abyte;
       result = 37 * result + (abool ? 0 : 1);
       result = 37 * result + (int) (along ^ (along >>> 32));
       result = 37 * result + Float.floatToIntBits(afloat);
       long tolong = Double.doubleToLongBits(adouble);
       result = 37 * result + (int) (tolong ^ (tolong >>> 32));
       result = 37 * result + aObject.hashCode();
       result = 37 * result + intsHashCode(ints);
       result = 37 * result + unitsHashCode(units);
       return result;
    }

    private int intsHashCode(int[] aints) {
       int result = 17;
       for (int i = 0; i < aints.length; i++)
           result = 37 * result + aints[i];
       return result;
    }

    private int unitsHashCode(Unit[] aUnits) {
       int result = 17;
       for (int i = 0; i < aUnits.length; i++)
           result = 37 * result + aUnits[i].hashCode();
       return result;
    }
}

你可能感兴趣的:(JAVA基础)