黑马程序员技术交流社区

标题: 【上海校区】java开发——为什么要重写equals()和hashCode()方... [打印本页]

作者: 不二晨    时间: 2019-2-22 16:31
标题: 【上海校区】java开发——为什么要重写equals()和hashCode()方...
结论:

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

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

举例:

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相同的元素集合,在哈希表中会以链表或者红黑树的形式储存!
---------------------
【转载,仅作分享,侵删】
作者:river66
原文:https://blog.csdn.net/river66/article/details/87803663
版权声明:本文为博主原创文章,转载请附上博文链接!


作者: 不二晨    时间: 2019-2-26 15:34
奈斯,感谢分享




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2