首先我们熟知HashSet集合中元素存储的特点:
1)不允许元素重复;
2)不会记录元素添加的先后顺序;
3)HashSet中比较两个对象是否相同,要使用equals()方法,不能使用 ==;
4)底层依然使用哈希表(散列)算法,其本质就是数组形式,采用此算法就为提高查询的效率;
5)插入速度也比较快,但适合于少量元素插入操作;一旦所存储元素个数满足(size * loadFoctor > size),哈希表就要扩容,此时操作速度极慢,性能就会降低!
由于HashSet集合中的元素不能重复存储,那应该怎样做呢?
1)先判断两个对象的hashCode()方法返回值是否相同,即存储的位置;
2)然后再判断两个对象的equals()方法返回值是否为true,即存储实际的对象值。
接下来我们就来讲解一下采用哈希表(散列)算法实现元素不可重复存储,具体的思想:
第一:
1)Set集合中元素没有顺序,不能重复;
2)元素重复是指:存储对象的重复;
3)何为对象的重复:内存中,所在内存编号一致(相同);
4)内存编号表示:哈希码值(哈希码值一般是 类名 和 对象所在内存地址的十六进制数字表示的集合)
第二:(现实共同观念)
现实生活中只要属性相同,我们就认为这两个对象就是同一个对象,但这与计算机比较同一个对象的方法不同(计算机使用内存地址,即哈希码值);Object类中的hashCode()方法是不可能返回两个相同的哈希码值(一个哈希码值唯一的标志了一个对象),即地址的唯一性。但是这样就不能让程序的运行符合现实生活(现实逻辑:属性相同的对象被看作是同一个对象)
于是就需要重写equals()和hashCode()方法,并且基本数据类型都重写了这两个方法!
第三:
重写这两个方法有什么用呢?
主要目的:属性相同的两个对象,返回的哈希码值是相同的!
程序向HashSet集合中添加一个元素时,先调用对象的hashCode()方法计算出该对象的哈希码值;
比较:
(1)如果该对象与集合中所存储的全部对象的哈希码值不一致,则该对象就不重复,计算出该对象在哈希表中的索引位置,直接添加;
(2)如果该对象与集合中存储的某一对象哈希码值一致(重码现象),那就需要通过equals()方法判断相同哈希码值的对象是否为同一对象(判断标准:属性是否相同);
a)相同对象,新值覆盖旧值;
b)不相同,在该索引位置,以头插的形式插入链表中。
第四:有两个疑问
(1)为什么哈希码值相同了,还有可能是不同的对象?
虽然重写hashCode()方法的主要目的:属性相同的两个对象,返回的哈希码值是相同的!
但是在重写hashCode()方法时,几乎所有的写法都无法避免一个bug:有一些对象(当然是不同的对象),会返回相同的哈希码(即重码),此时就需要借助equals()方法;
在哈希码相同的情况下,再使用equals()方法判断两个对象的属相是否一样,就可以做到万无一失了
(2)为什么经过比较哈希码值,还需要借助equals()方法判断呢?
HashSet集合底层采用了哈希算法实现,多个不同的对象可能返回的哈希码值不同,但是通过计算得到的哈希表中的索引位置相同,这样就再次需要通过equals()方法来判断这两个对象的属性值是否相同,比较完再做相应的处理!
总思路:哈希码不同时,则必为不同的对象,重写hashCode()方法时,哈希码相同(可能出现重码现象),则根据euqals()方法判断是否新值覆盖旧值;两者都是以链表头插方式!在HashSet、HashMap、HashTable中都存在这一问题!