java进阶—Hashcode与equals深度解析

1.深入理解equals():
• 在我的一篇博客“”和.equals()的区别中向读者提出提醒: Object类中的equals方法和“”是一样的,没有区别,即俩个对象的比较是比较他们的栈内存中存储的内存地址。而String类,Integer类等等一些类,是重写了equals方法,才使得equals和“==不同”,他们比较的是值是不是相等。所以,当自己创建类时,自动继承了Object的equals方法,要想实现不同的等于比较,必须重写equals方法。

来自 https://blog.****.net/qq_21688757/article/details/53067814
Java对象的eqauls方法和hashCode方法是这样规定的:
1、相等(相同)的对象必须具有相等的哈希码(或者散列码)。
2、如果两个对象的hashCode相同,它们并不一定相同。

来自 https://www.cnblogs.com/Qian123/p/5703507.html

Java对于eqauls方法和hashCode方法是这样规定的:
  1.如果两个对象相同,那么它们的hashCode值一定要相同;
2.如果两个对象的hashCode相同,它们并不一定相同(这里说的对象相同指的是用eqauls方法比较)。
如不按要求去做了,会发现相同的对象可以出现在Set集合中,同时,增加新元素的效率会大大下降。
3.equals()相等的两个对象,hashcode()一定相等;equals()不相等的两个对象,却并不能证明他们的hashcode()不相等。
换句话说,equals()方法不相等的两个对象,hashcode()有可能相等(我的理解是由于哈希码在生成的时候产生冲突造成的)。反过来,hashcode()不等,一定能推出equals()也不等;hashcode()相等,equals()可能相等,也可能不等。
在object类中,hashcode()方法是本地方法,返回的是对象的地址值,而object类中的equals()方法比较的也是两个对象的地址值,如果equals()相等,说明两个对象地址值也相等,当然hashcode()也就相等了;在String类中,equals()返回的是两个对象内容的比较,当两个对象内容相等时,Hashcode()方法根据String类的重写代码的分析,也可知道hashcode()返回结果也会相等。以此类推,可以知道Integer、Double等封装类中经过重写的equals()和hashcode()方法也同样适合于这个原则。当然没有经过重写的类,在继承了object类的equals()和hashcode()方法后,也会遵守这个原则。

来自 https://www.cnblogs.com/Qian123/p/5703507.html

Hashset、Hashmap、Hashtable与hashcode()和equals()的密切关系
Hashset是继承Set接口,Set接口又实现Collection接口,这是层次关系。那么Hashset、Hashmap、Hashtable中的存储操作是根据什么原理来存取对象的呢?
下面以HashSet为例进行分析,我们都知道:在hashset中不允许出现重复对象,元素的位置也是不确定的。在hashset中又是怎样判定元素是否重复的呢?在java的集合中,判断两个对象是否相等的规则是:
1.判断两个对象的hashCode是否相等
如果不相等,认为两个对象也不相等,完毕
如果相等,转入2
(这一点只是为了提高存储效率而要求的,其实理论上没有也可以,但如果没有,实际使用时效率会大大降低,所以我们这里将其做为必需的。)
2.判断两个对象用equals运算是否相等
如果不相等,认为两个对象也不相等
如果相等,认为两个对象相等(equals()是判断两个对象是否相等的关键)
为什么是两条准则,难道用第一条不行吗?不行,因为前面已经说了,hashcode()相等时,equals()方法也可能不等,所以必须用第2条准则进行限制,才能保证加入的为非重复元素。

来自 https://www.cnblogs.com/Qian123/p/5703507.html

为什么会生成不同的哈希码值呢?上面我们在比较s1和s2的时候不是生成了同样的哈希码吗?原因就在于我们自己写的Student类并没有重新自己的hashcode()和equals()方法,所以在比较时,是继承的object类中的hashcode()方法,而object类中的hashcode()方法是一个本地方法,比较的是对象的地址(引用地址),使用new方法创建对象,两次生成的当然是不同的对象了,造成的结果就是两个对象的hashcode()返回的值不一样,所以Hashset会把它们当作不同的对象对待。
怎么解决这个问题呢?答案是:在Student类中重新hashcode()和equals()方法。

来自 https://www.cnblogs.com/Qian123/p/5703507.html

最后我们在来看一下hashCode造成的内存泄露的问题:看一下代码:
package com.weijia.demo;
import java.util.HashSet;
public class Demo {
public static void main(String[] args){
HashSet set = new HashSet();
RectObject r1 = new RectObject(3,3);
RectObject r2 = new RectObject(5,5);
RectObject r3 = new RectObject(3,3);
set.add(r1);
set.add(r2);
set.add(r3);
r3.y = 7;
System.out.println(“删除前的大小size:”+set.size());
set.remove(r3);
System.out.println(“删除后的大小size:”+set.size());
}
}

来自 https://blog.****.net/qq_21688757/article/details/53067814

运行结果:
删除前的大小size:3
删除后的大小size:3
擦,发现一个问题了,而且是个大问题呀,我们调用了remove删除r3对象,以为删除了r3,但事实上并没有删除,这就叫做内存泄露,就是不用的对象但是他还在内存中。

来自 https://blog.****.net/qq_21688757/article/details/53067814
java进阶—Hashcode与equals深度解析

HashMap的底层结构是数组+链表
数组:
HashMap以键值对存储数据,其中Key-Value都是Map.Entry中的属性。数组的值对应Value值,数组的下标对应Key值,对应过程如下:将Key值进行Hash后得到Hash值,Hash值得范围大概40亿,很明显,内存存不下,进行转换,对数组的长度(即hashmap的长度)进行取模运算,得到的余数才用来做要存放的位置(即对应的数组下标)。[这也解释了HashMap的长度为什么是2的幂次方,为了提高取模运算的效率]。

来自 https://blog.****.net/linton1/article/details/90084524

那个二进制的与运算实现的就是取余功能,n % hash 与 (n - 1) & hash等价,与运算效率更高。但是n必须是2的倍数,这也是为什么HashMap总是使用2的幂作为哈希表的大小

来自 https://blog.****.net/linton1/article/details/90084524