Set接口
Collection集合(接口)的接口下面有2个直接的子接口:
|-----List集合(接口):可以保存重复元素,拥有下标,存储有序,可以存储多个null元素。
|-----ArrayList类:底层是可变数组,根据下标进行操作,查询效率快,增删效率低。
|-----LinkedList类:底层是链表,根据链表的头尾进行操作,增删效率快,查询效率低。
|-----Set集合(接口):不能保存重复元素,没有下标。可以存储null但只能有一个。并且不保证存取的顺序,也就是说对于集合set进行存取操作的时候都没有任何顺序,没有任何规律而言。
|-----HashSet类
|-----LinkedHashSet类
说明:
1)Set接口中没有自己的特有函数,所有的函数全部来自于Collection接口。
2)Set集合没有角标,只能通过Iterator迭代器遍历获取集合中的元素。
哈希表:
它是一个数据结构,底层依赖的是数组,只是不按照数组的下标操作数组中的元素。需要根据数组中存储的元素的哈希值进行元素操作。
哈希表的存储过程:
1)哈希表底层是一个数组,我们必须知道集合中的一个对象元素到底要存储到哈希表中的数组的哪个位置,也就是需要一个下标。
2)哈希表会根据集合中的每个对象元素的内容计算得出一个整数值。由于集合中的对象元素类型是任意的,而现在这里使用的算法必须是任意类型的元素都可以使用的算法。能够让任意类型的对象元素都可以使用的算法肯定在任意类型的对象所属类的父类中,即上帝类Object中,这个算法就是Object类中的hashCode()函数。
结论:要给HashSet集合中保存对象,需要调用对象的hashCode函数。
解释说明:
通过查阅API得知,使用Object的任意子类对象都可以调用Object类中的hashCode()函数并生成任意对象的哈希码值。
3)hashCode算法,得到一个整数,但是这个整数太大了,这个值不能直接作为数组下标的。所以底层还会对这个值结合数组的长度继续计算运行,得到一个在0~数组长度-1之间的整数,这样就可以作为数组的下标了。
问题1:使用hashCode函数生成的一个过大整数是用什么算法将将生成哈希码值变成0~数组长度-1之间的数字呢?
其中一种最简单的算法是可以实现的,举例:假设底层哈希表中数组长度是5,那么下标的范围是0~4,
所以我们这里可以使用生成的哈希码值(过大的整数)对数组长度取余数,我们发现任何数在这里对5取余都是在 0 1 2 3 4 之间,所以这样就可以获取到0~数组长度-1之间的下标了。
问题2:如果数据过多,HashSet底层的数组存储不下,怎么办?
hashSet集合底层数组初始容量是16,如果大小不够,那么会继续新创建一个数组,新数组大小等于原来数组的大小*0.75+原来数组的大小。
4)如果上述做法已经计算出底层数组的下标位置,那么就要判断计算出的下标位置是否已经有元素了:
A.如果下标对应的位置没有元素:直接存储数据;
B.如果下标对应的位置有元素:这时就必须调用对象的equals()函数比较两个对象是否相同:
如果结果是true:相同,直接将要添加的数据丢弃;
如果结果是false:不相同,那直接将数据存储到数组当前空间位置;
但是当前位置已经存在元素了,怎么将后来的数据存储到数组中呢?
这里需要使用类似链表的结构了,在当前位置上在画出来一个空间,然后将当前的对象数据保存到新划出来的空间中,在原来的空间中设置一个引用变量记录着新划分空间的地址,如果后面还有数据要存储当前空间,做法和上述相同。
最终结论:哈希表底层通过hashCode和equals算法结合,来保证对象数据在HashSet集合中不重复唯一,并且存储的顺序不固定。
HashSet总结
HashSet集合存储对象的时候:
1、HashSet集合的底层使用的哈希表结构。那么就要求存放的对象必须具备hashCode功能。由于任何一个类的父类都是Object类,而hashCode函数定义在了Object类中,因此所有的对象都具备hashCode功能。
2、如果我们要把一个对象可以正确的存放在HashSet集合中,这个对象所属的类一般都需要复写hashCode函数。建立本类自己的计算哈希值的方式。
3、如果在HashSet集合中要保证对象唯一,不能仅仅依靠hashCode函数,还要依赖于对象的equals函数,当hashCode函数计算出来的哈希值相同的时候,还要调用equals方法比较2个对象是否相同。
4、要求在向HashSet集合中存储自己定义一个类对象的时候,那么必须在这个自定义类中复写Object类中的hashCode和equals函数。
LinkedHashSet集合:它的底层使用的链表+哈希表结构。它和HashSet集合的区别是LinkedHashSet是一个可以保证存取顺序的集合,并且LinkedHashSet集合中的元素也不能重复。
特点:
A:存取有序(底层有一个链接表)
B:保证元素的唯一(哈希表)
C:线程不安全,效率高