java开发——为什么要重写equals()和hashCode()方法?

结论:

1、重写equals()是为了实现自己的区分逻辑。

2、重写hashCode()是为了提高hash tables的使用效率,以及和equals()保持一致(看下面hashcode的第三点)。

举例:

1、String重写Object的equals方法

2、HashSet是如何保证存储的元素不同的?

 

首先明白这两个方法是什么来的?

equals()和hashCode()是Object的两个方法。作用有两个:

1、区分两个Object

2、给子类重写,让子类去实现自己的区分逻辑。

看下Object的源码是如何区分两个Object的。

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

可以看出除了==this,否则两个对象不相同。

对于hashCode,源码注释中这样解释的:

1、它是一个代表Object的整数int值,它有利于哈希表的存储使用。(重要)

2、无论何时调用同一对象此方法,返回值都应是相同的。(equals用到的比较信息没有被修改的情况下)

3、如果equals()方法确定了两个对象相等,则这两个对象的hashCode必须返回相同的值(这点就可以决定重写equals()方法就必须重写hashCode方法了)

4、如果equals()方法确定了两个对象不相等,这个两个对象的hashCode还是有可能相等的。但是我们强烈建议不同的对象应该有着不同的hashCode,这样可以提高hash tables的使用效率。

综上,为什么要重写equals()和hashCode()方法的原因有两个:

1、重写equals()是为了实现自己的区分逻辑。

2、重写hashCode()是为了提高hash tables的使用效率。

下面是举例:重写的重要性

例子1:String重写Object的equals方法,源码如下:

public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String) anObject;
            int n = count;
            if (n == anotherString.count) {
                int i = 0;
                while (n-- != 0) {
                    if (charAt(i) != anotherString.charAt(i))
                            return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

public int hashCode() {
        int h = hash;
        if (h == 0 && count > 0) {
            for (int i = 0; i < count; i++) {
                h = 31 * h + charAt(i);
            }
            hash = h;
        }
        return h;
    }

可以看出在重写equals()和hashCode()方法时,字符串中的每一个字符都用上了。这样,"abc"和"adcd"的hashCode肯定不相同。"abc".equals("abc")返回true是我们要的结果,符合我们区分逻辑。而不是像Object的equals方法那样“this == obj”引用相等才相同。所以String需要重写Object的equals方法。

例子2:HashSet是如何保证存储的元素不同的?

看下add方法,源码如下:

private transient HashMap<E,Object> map;
private static final Object PRESENT = new Object();

public boolean add(E e) {
     return map.put(e, PRESENT)==null;
}

可以看出,HashSet底层是依靠HashMap实现的,而我们知道,HashMap的键值是不可以重复的(可以为null)。重点来了,HashMap保证键值不重复就是依靠对象的equals()和hashCode()方法。

看HashMap的put方法部分伪代码:

1、key是新加入的对象
2、hash是key的哈希值
3、e是哈希表中的每一个元素
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
   1、新值覆盖旧值(这个都是同一个Object PRESENT = new Object();)
   2、返回旧值
}
否则新增新的结点存储新值。

可以看出,在HashMap新增key-value的时候,如果hashCode和equals都相同,HashMap不会新增新的结点存储新值。而是在原来的key上覆盖掉value值。那么对于HashSet来说,也就是没有新增元素,因为HashSet的元素来源于HashMap的key集合

 也就是说HashSet保证存储的元素的不同,是通过equals()和hashCode()方法来保证的。这就是为什么我们要重写equals()和hashCode()方法的重要原因之一。

补充:对于equals不相同而hashCode相同的元素集合,在哈希表中会以链表或者红黑树的形式储存!

 

觉得有用的老铁们赞一下,谢谢!

  • 28
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值