上一篇白話HashMap源碼(上)看過了HashMap的基本結構和主要的操作方法Get、Put、Remove的源碼。瞭解了HashMap的存儲方式,數組加鏈表,與空間的自增長思路,每次空間翻倍舊數據重新裝填。
這一篇主要看一下遍歷相關的源碼。
遍歷前需要了解的方法
containsKey
@Override public boolean containsKey(Object key) {
if (key == null) {//key爲null的條件
return entryForNullKey != null;
}
int hash = Collections.secondaryHash(key);
HashMapEntry<K, V>[] tab = table;
for (HashMapEntry<K, V> e = tab[hash & (tab.length - 1)];
e != null; e = e.next) {//由key找到數組內的具體項,遍歷鏈表
K eKey = e.key;
if (eKey == key || (e.hash == hash && key.equals(eKey))) {
return true;
}
}
return false;
}
containsValue
橫向遍歷數組,縱向遍歷鏈表,如果沒有找到別忘了HashMap還維護了一個entryForNullKey。
但是區分了value爲null的條件,因爲null.equals會報錯。
@Override public boolean containsValue(Object value) {
HashMapEntry[] tab = table;
int len = tab.length;
if (value == null) {
for (int i = 0; i < len; i++) {
for (HashMapEntry e = tab[i]; e != null; e = e.next) {
if (e.value == null) {
return true;
}
}
}
return entryForNullKey != null && entryForNullKey.value == null;
}
// value is non-null
for (int i = 0; i < len; i++) {
for (HashMapEntry e = tab[i]; e != null; e = e.next) {
if (value.equals(e.value)) {
return true;
}
}
}
return entryForNullKey != null && value.equals(entryForNullKey.value);
}
遍歷keySet
由於keySet、values、entrySet的實現邏輯一樣,此處就只看keySet的源碼
第一步返回Set
@Override public Set<K> keySet() {
Set<K> ks = keySet;
return (ks != null) ? ks : (keySet = new KeySet());
}
可以看到返回了一個KeySet對象
private final class KeySet extends AbstractSet<K> {
public Iterator<K> iterator() {
return newKeyIterator();
}
public int size() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
public boolean contains(Object o) {
return containsKey(o);
}
public boolean remove(Object o) {
int oldSize = size;
HashMap.this.remove(o);
return size != oldSize;
}
public void clear() {
HashMap.this.clear();
}
}
裏面包含了常用的方法,size方法直接用HashMap對象的size,isEmpty一樣,contains則是使用HashMap的containsKey方法,remove也是調用HashMap對象的方法,關鍵的是iterator這個迭代器。
第二步迭代器
Iterator<K> newKeyIterator() { return new KeyIterator(); }
源碼很簡單,直接返回KeyIterator對象,繼續向下走。
private final class KeyIterator extends HashIterator
implements Iterator<K> {
public K next() { return nextEntry().key; }
}
返回了nextEntry.key,看名字應該是下一項,但nextEntry哪裏來的呢?
看KeyIterator 的繼承,HashIterator它是整個HashMap迭代的實現者
第三步迭代的實現者
private abstract class HashIterator {
int nextIndex;
HashMapEntry<K, V> nextEntry = entryForNullKey;
HashMapEntry<K, V> lastEntryReturned;
int expectedModCount = modCount;
HashIterator() {//初始時,nextEntry指到第一項
if (nextEntry == null) {
HashMapEntry<K, V>[] tab = table;
HashMapEntry<K, V> next = null;
while (next == null && nextIndex < tab.length) {
next = tab[nextIndex++];
}
nextEntry = next;
}
}
public boolean hasNext() {
return nextEntry != null;
}
HashMapEntry<K, V> nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
if (nextEntry == null)
throw new NoSuchElementException();
HashMapEntry<K, V> entryToReturn = nextEntry;
HashMapEntry<K, V>[] tab = table;
HashMapEntry<K, V> next = entryToReturn.next;
//如果這一項是鏈表,則返回鏈表的下一項,否則返回數組的下一項
while (next == null && nextIndex < tab.length) {
next = tab[nextIndex++];
}
nextEntry = next;
return lastEntryReturned = entryToReturn;
}
public void remove() {
if (lastEntryReturned == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
HashMap.this.remove(lastEntryReturned.key);
lastEntryReturned = null;
expectedModCount = modCount;
}
}
至此HashMap的源碼就看完了,至於其他功能類的方法,相信有了這兩篇的基礎想看懂原理應該不難。
HashSet的內部實現其實也是HashMap,只是HashSet存入的value作爲key,HashSet對象作爲value存入HashMap。