首先要知道为什么要重写equals方法?

那么先引出 == 和equals的区别:

  • 如果两个引用类型变量使用==运算符,那么比较的是地址,它们分别指向的是否是同一地址的对象,结果一定是false,因为两个对象地址必然不同。
  • ==不能实现比较对象的值是否相同。
  • 所有对象都有equals方法,默认是Object类的equals,其结果与==一样。
  • 如果希望比较对象的值相同,必须重写equals方法。

所以我们想要比较两个对象的内容,就必须重写equals方法

那么为什么重写equals方法时必须重写hashcode方法?

是为了提高效率,采取重写hashcode方法,先进行hashcode比较,如果不同,那么就没必要再进行equals的比较了,这样就大大减少了equals比较的次数

本来equals方法和hashcode()是不相关的,但是由于我们会在集合中放入我们创建的对象,这样由于存储结构的问题,导致在查找时二者产生了关联。

public static void main(String[] args) {
        Map<MyObject, Integer> map = new HashMap<MyObject, Integer>();
        HashMapTest instance = new MyObject("a");
        map.put(instance, 1);
        Integer value = map.get(new MyObject("a"));
        if (value != null) {
            System.out.println(value);
        } else {
            System.out.println("value is null");
        }
    } 
//程序运行结果: value is null

我们往一个HashMap中,放入一个自定义对象 MyObject(“a”)的实例,假设我们重写了equals,然后通过get()方法获取,为何结果为空?

原因是由于hashmap的存储结构是一个hash,作用是方便快速查找,导致如果hashcode不同(hashcode默认和内存相关),2个对象new 出来,内存地址不同,那么无法找到对应的桶,就无法找到对应的对象,因此,结果就为null。所以为了保险起见,所有的自定义类,如果重写了equals方法时必须重写hashcode方法

这里引出来什么是Hash算法

Hash是散列的意思,就是把任意长度的输入,通过散列算法变换成固定长度的输出,该输出就是散列值。关于散列值,有以下几个关键结论:

  • 如果散列表中存在和散列原始输入K相等的记录,那么K必定在f(K)的存储位置上 ,即多次计算hash值一定相同
  • 不同关键字经过散列算法变换后可能得到同一个散列地址,这种现象称为碰撞
  • 如果两个Hash值不同(前提是同一Hash算法),那么这两个Hash值对应的原始输入必定不同

总结:

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