HashMap和HashSet是Map接口、Set接口常用的實現類,因此研究這兩個實現原理有助於更好的使用它們,並理解他們的區別。
下面先來分析HashMap的底層實現。
Map中存儲的是<Key,value>對,每個<Key,value>對都以Map.Entry的數據結構存儲,然後用一個table數組來存儲Entry,該table就是根據實例化的參數創建的數組。
存儲的位置則按照Key計算的hash返回值來計算。
以下是Java源代碼HashMap類中添加元素的函數。
void addEntry(int hash, K key, V value, int bucketIndex) {
if ((size >= threshold) && (null != table[bucketIndex])) {
resize(2 * table.length);
hash = (null != key) ? hash(key) : 0;
bucketIndex = indexFor(hash, table.length);
}
createEntry(hash, key, value, bucketIndex);
}
void createEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<>(hash, key, value, e);
size++;
}
void resize(int newCapacity) {
Entry[] oldTable = table;
int oldCapacity = oldTable.length;
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return;
}
Entry[] newTable = new Entry[newCapacity];
transfer(newTable, initHashSeedAsNeeded(newCapacity));
table = newTable;
threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
}
由此可以看出,當數組的元素已滿時,則double其容量。
然後在bucketindex位置創建一個新的Entry,如果此處已經有一個Entry,則新的Entry指向原來的Entry,產生一個Entry鏈,並且新放入的Entry永遠處於Entry鏈的起端,位於數組bucket中。
當查詢元素時,調用get()函數。
final Entry<K,V> getEntry(Object key) {
if (size == 0) {
return null;
}
int hash = (key == null) ? 0 : hash(key);
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
}
return null;
}
可見新根據key計算的hash值去找相應的bucket,如果該bucket只有一個元素則直接返回該Value,如果存在一個Entry鏈的話,必須遍歷整個Entry鏈才能找到相應的value值。
由此可見,當table中的每個bucket中都只有一個Entry元素時,該hashMap的性能最好。
明白了HashMap的原理,HashSet就迎刃而解了。因爲HashSet的底層就是用HasHMap來實現的,即把HashMap中的key來存儲元素即實現了set.
我們來看一下HashSet的源碼便知。
public HashSet() {
map = new HashMap<>();
}
public HashSet(Collection<? extends E> c) {
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<>(initialCapacity, loadFactor);
}
public HashSet(int initialCapacity) {
map = new HashMap<>(initialCapacity);
}
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}