HashSet如何保证元素的唯一性?
HashSet如何保证元素的唯一性?
HashSet存储自定义对象来保证唯一性。
需求:如果两个对象的成员变量值都相同,则为同一个对象。也就是同一个元素。
String:
name,age
按照我们的想法,Set集合应该给我们去掉了重复的元素。
但事实上,这个元素还存在,这是为什么呢?
因为两个对象的成员变量值相同,这两个对象未必是同一个对象。
只不过我们的需求认为他是同一个对象。
而我们又知道要比较对象的成员变量值,就必须重写equals()。
我们重写了equals()后,并没有实现去掉重复元素。那么,为什么呢?
我们做一个简单的检测,看equals()方法有没有被执行。
通过一个简单的测试,我们发现程序并没有去执行equals(),这又是为什么呢?
看add方法的源码。
最终我们发现了罪魁祸首:
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
{
就没有添加到集合中。
}
这个判断分为左边和右边两部分:
左边:
e.hash == hash
左边比较的是哈希值,而对象的哈希值一般来说肯定不一样。
所以,左边永远是false。
所以,右边永远执行不了。
右边:
((k = e.key) == key || key.equals(k))
分为左边和右边:
左边:
//k是s1的地址值
//key是s2的地址值
(k = e.key) == key
由于不同对象的地址值肯定不一样,所以,左边一般肯定是false。
右边:
key.equals(k)
HashSet集合的底层数据结构是哈希表。
哈哈希表的存储依赖两个方法:
hashCode()和equals()。
执行顺序:
首先判断对象的哈希值是否相同,
如果相同,就继续执行equals()方法。
如果equals()方法返回true,说明元素重复了。就不添加。
如果equals()方法返回false,说明元素没有重复的,就添加到集合中。
如果不同,就直接添加到集合中。
以后你看到HashXxx结构的集合,要知道,这种结构底层是哈希表。(是一个元素是链表的数组)
它依赖hashCode()和equals()方法。
自动生成即可。
HashSet保证元素唯一性原理图解
HashSet保证元素唯一性的源码
class HashSet {
private transient HashMap<E,Object> map;
private static final Object PRESENT = new Object();
public HashSet() {
map = new HashMap<>();
}
public boolean add(E e) { //e -- s1,s2
return map.put(e, PRESENT)==null;
}
}
class HashMap {
public V put(K key, V value) {
//key -- e -- s1,s2
//value -- new Object()
//哈希表是否为空。
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
//不执行这个if判断。因为我们的数据不是null。
if (key == null)
return putForNullKey(value);
int hash = hash(key); //可以理解为返回该对象的哈希值。
//s2的哈希值
int i = indexFor(hash, table.length); //在哈希表中查找元素出现的位置。
//第一次,哈希表没有元素,所以,第一次存储元素,不需要执行这里。
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
//e -- s1
//this -- s2
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++; //统计++
addEntry(hash, key, value, i); //添加元素。
return null;
}
//这个hash方法的返回值和k的hashCode()方法相关。
final int hash(Object k) {
//k -- s1
int h = hashSeed;
if (0 != h && k instanceof String) {
return sun.misc.Hashing.stringHash32((String) k);
}
h ^= k.hashCode(); //这个跟对象的hashCode()相关。
// This function ensures that hashCodes that differ only by
// constant multiples at each bit position have a bounded
// number of collisions (approximately 8 at default load factor).
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
}