1.什么是HashSet:
-
一个单列集合;
-
不保证set的迭代顺序,特别他不保证该顺序恒久不变;
-
前身是HashTable(哈希表:线程安全);
-
允许使用null元素
-
此时先是不同步的,所以线程不安全。
2.HashSet底层:
-
HashSet的底层其实是一个HashMap;
-
HashSet数据结构:一个对象数组,数组中存有链表;
-
HashSet初始容量为16,加载因子为0.75;
问题1.为什么说HashSet的值是不可重复的呢?
-
我们都知道HashMap是一个双列集合,而HashSet是单列集合;
-
当HashSet创建一个HashMap的时候,把HashSet只在HashMap的key一列存值;
-
而HashMap的key值是不可重复的,也就是说HashSet的值是不重复的。
问题2:为什么说HashSet不保证原始顺寻恒久不变呢?
-
由于HashSet的加载因子为0.75,也就是说当HashSet的内存使用率为75%是一个分割线;
-
当内存使用率达到75%,那HashSet集合中的对象数组将进行扩容;
-
这时候HashSet中的
所有元素的顺序将尽数被打乱重新存入到HashSet中。
问题3:
HashSet如何存值?
-
首先每一个对象都有自己的HashCode;
-
HashSet将依照这个HashCode对自己的容量进行取余;
-
根据这个余数,HashSet将这个值存入到对应的对象数组“下标”的链表中。
-
而这里又会出现一个小问题4(当两个对象,HashCode不相同,但是内容却完全相同的情况下如何存入HashSet中?数据去重)。
从问题三咱们能引申出两个问题:
代码如下:
Person p1 = new Person("张三",18);
Person p2 = new Person("张三",18);
HashSet set = new HashSet();
set.add(p1);
set.add(p2);
问题4:
如果存入两个内容完全相同的对象,而HashSet要求我们数值不可重复,那我们
如何去重
?
-
首先我们知道这两个对象虽然内容一样,但是也没有完全一样,因为他们引用的内存空间不是同一块内存空间;
-
所以两个不一样的对象,他们的HashCode也是不一样的(极大概率);
-
这也就意味着,在实体类中没有重写hashCode()和equals()方法的时候,他们都能存到HashSet中,而且不被覆盖;
-
所以我们在
创建实体类的时候要重写hashCode()和equals(),确保两个内容完全相同的对象只能插入一个到HashSet中。
问题5:内存泄漏
以实例为例:
-
在我们重写了hashCode()和equals()方法之后,对象会根据hashCode()方法,将对象的属性加入到一个算术中计算,得出hashCode;
-
当我们把这个对象存入HashSet后,我们突然要更改这个对象的属性,当然这个时候我们还能找到这个对象并更改其内容;
-
而在我们更改了这个对象的属性之后他的HashCode也会随之改变;
-
当我们再次访问HashSet来查找这个对象的时候,新生成的HashCode会让我们无法找到原来的对象,比如删除该对象就无法删除;
-
这,就是内存泄漏!
-
在重写HashCode()方法的时候,把可能之后会有变化的属性去掉,只留下不会进行更改的属性。