文章同步更新在個人博客:HashSet怎樣保證元素不重復
都知道HashSet中不能存放重復元素,有時候可以用來做去重操作等。但是其內部是怎么保證元素不重復的呢?下面從源碼去看看。
打開HashSet源碼,發現其內部維護了一個HashMap:
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
{
static final long serialVersionUID = -5024744406713321676L;
private transient HashMap<E,Object> map;
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
/**
* Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
* default initial capacity (16) and load factor (0.75).
*/
public HashSet() {
map = new HashMap<>();
}
...
}
HashSet的構造方法其實就是在內部實例化了一個HashMap對象。其中還會看到一個static final的PRESENT變量,這個稍候再說,其實沒什么實際用處。
想知道為什么HashSet不能存放重復對象,那么第一步當然是看它的add方法怎么進行的判重,代碼如下:
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
。。。好吧,就把元素存放在了map里面。但是值得注意的是元素值作為的是map的key,map的value則是前面提到的PRESENT變量,這個變量只作為放入map時的一個占位符而存在,所以沒什么實際用處。
其實,這時候答案已經出來了:HashMap的key是不能重復的,而這里HashSet的元素又是作為了map的key,當然也不能重復了。
HashSet怎么做到保證元素不重復的原因找到了,文章也就結束了。。。等等,順便看一下HashMap里面又是怎么保證key不重復的吧,代碼如下:
public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
if (key == null)
return putForNullKey(value);
int hash = hash(key);
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
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;
}
其中最關鍵的一句:
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
調用了對象的hashCode和equals方法進行的判斷,所以又得出一個結論:若要將對象存放到HashSet中並保證對象不重復,應根據實際情況將對象的hashCode方法和equals方法進行重寫
