HashMap類聲明
HashMap類聲明如下:
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {
//……
//……
}
從定義中可以看出,哈希表是支持克隆、序列化的。
HashMap繼承了AbstractMap,除了toString方法,HashMap重寫了其他所有的AbstractMap方法。
HashMap實現的克隆是淺複製。
HashMap自定義了序列化相關的方法。
HashMap實現原理
分析HashMap代碼之前先了解它的實現原理,HashMap是通過拉鍊法實現的,何謂拉鍊法?HashMap是一個數組,數組的元素是一個鏈表,鍵值對是存儲在該鏈表中的。通過這種方法能夠解決哈希衝突問題,哈希碼相同的鍵對應的鍵值對會存儲在同一個鏈表中。拉鍊表示例圖如下:
該哈希表的容量爲8,鍵值對數量爲6。目前該哈希表中只有索引0、4、7處有數據。其中e0和e1節點鍵的哈希碼相同,e3、e4、e5節點鍵的哈希碼相同。
HashMap中有加載因子的概念,它代表了哈希表有多滿的一種程度,這個值默認是0.75。當哈希表的鍵值對數量大於等於加載因子和哈希表容量乘積的時候,將會觸發重哈希。例如,上圖哈希表重哈希的鍵值對數量臨界值爲6(8*0.75=6)。因爲該哈希表鍵值對數量已經爲6,此時若繼續往該哈希表添加鍵值對,將會觸發重哈希。
加載因子是爲了哈希表的效率而提出的,這會保證哈希表中的鏈表不至於太長,從而減少衝突,增加查詢效率。平均來說,哈希表中每個鏈表長度都將小於1,因此查找效率非常高。加載因子越大,衝突的可能性就越大,加載因子越小,衝突的可能性就越小。
有了實現原理的基本瞭解,接下來分析HashMap的源碼實現。
HashMap源碼分析
成員變量
HashMap包括了很多成員變量,下面代碼詳細註釋了每個字段的含意:
//哈希表的默認容量是16
//注意:哈希表容量必須是2的n次冪,這是爲了更快的定位哈希索引
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
//哈希表的最大容量
static final int MAXIMUM_CAPACITY = 1 << 30;
//哈希表的加載因子默認值0.75
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//若沒有對哈希表執行任何操作,數組是一個空值,只有在需要的時候纔會去分配內存
static final Entry<?,?>[] EMPTY_TABLE = {};
//哈希表的數組,其中每個元素都是一個鏈表,哈希表數組大小必須是2的n次冪
transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;
//哈希表的鍵值對數量
transient int size;
//哈希表擴容的臨界值,大小等於加載因子*哈希表的容量
int threshold;
//加載因子
final float loadFactor;
//哈希表結構性改變的次數,結構性改變指的是改變了哈希表鍵值對數量
//該字段主要用在迭代器的快速失敗策略(fail-fast),後面還會說明
transient int modCount;
//默認擴容的臨界值
static final int ALTERNATIVE_HASHING_THRESHOLD_DEFAULT = Integer.MAX_VALUE;
//一個隨機的值,用於計算key的哈希值
transient int hashSeed = 0;
//鍵值對的Set
private transient Set<Map.Entry<K,V>> entrySet = null;
Holder靜態內部類
//HashMap的靜態內部類,主要是去獲取jdk內置的擴容臨界值
private static class Holder {
static final int ALTERNATIVE_HASHING_THRESHOLD;
static {
//取jdk內置的擴容臨界值
String altThreshold = java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction(
"jdk.map.althashing.threshold"));
int threshold;
try {
threshold = (null != altThreshold)
? Integer.parseInt(altThreshold)
: ALTERNATIVE_HASHING_THRESHOLD_DEFAULT;
// disable alternative hashing if -1
if (threshold == -1) {
threshold = Integer.MAX_VALUE;
}
if (threshold < 0) {
throw new IllegalArgumentException("value must be positive integer.");
}
} catch(IllegalArgumentException failed) {
throw new Error("Illegal value for 'jdk.map.althashing.threshold'", failed);
}
ALTERNATIVE_HASHING_THRESHOLD = threshold;
}
}
構造函數
//包含初始容易和加載因子的構造函數
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
//HashMap的容量不能大於MAXMUM_CAPACITY
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
threshold = initialCapacity;
//HashMap的init方法其實是個空方法
init();
}
//根據指定初始容量、默認加載因子創建一個空的HashMap
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
//根據默認容量和默認加載因子創建一個空的HashMap
public HashMap() {
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}
//根據指定的HashMap創建一個新的HashMap,新的HashMap將擁有指定HashMap所有的鍵值對。
//新HashMap的初始容量根據指定HashMap的鍵值對數量決定,加載因子取默認值
public HashMap(Map<? extends K, ? extends V> m) {
this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
//創建好空的HashMap後,根據擴容臨界值擴容該HashMap
inflateTable(threshold);
//將指定哈希表m的所有鍵值對添加到該哈希表中
putAllForCreate(m);
}
擴容方法
//大於等於number的第一個2的n次冪的值,這個保證了哈希表的容量是2的n次冪
private static int roundUpToPowerOf2(int number) {
return number >= MAXIMUM_CAPACITY
? MAXIMUM_CAPACITY
: (number > 1) ? Integer.highestOneBit((number - 1) << 1) : 1;
}
//擴容哈希表
private void inflateTable(int toSize) {
int capacity = roundUpToPowerOf2(toSize);
threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
table = new Entry[capacity];
//初始化哈希種子
initHashSeedAsNeeded(capacity);
}
Integer.highestOneBit方法
上面代碼片斷使用了Integer.highestOneBit方法,這片代碼挺有意思的,看下源碼實現:
public static int highestOneBit(int i) {
i |= (i >> 1);
i |= (i >> 2);
i |= (i >> 4);
i |= (i >> 8);
i |= (i >> 16);
return i - (i >>> 1);
}
這個代碼片斷返回不大於參數i的最大的2的n次冪的值。例如如果傳入參數17,返回16(2的4次冪)。傳入參數16,返回16。傳入參數15,返回8。(確實很巧妙~)
這斷代碼怎麼實現的呢?是將參數i的二進制最左邊爲1的位的後面都設置爲0而得出的,例如對於10進制數17,它的二進制爲10001,將最高位1的右邊都設置爲0,變爲10000,也就是10進制16。同樣對於10進制數15,它的二進制數爲1111,將最高位1的右邊都設置爲0,變爲1000,也就是10進制8。
再來看這斷代碼的實現,首先將i右移1位並和原來的i作或操作,結果就是最高位爲1的後面那一位也將變爲1,現在最高位爲1的連續兩位都是1。接着繼續將i右移2位並和原來的i作或操作,結果就是最高位爲1的連續4位都是1。以此類推,直到最高位爲1的後面都變爲1。最後將該值減去將最高位設置爲0的值,也就只保留了最高位爲1的值。
例如對於i=10001(10進製爲17):
i |= (i >> 1),即i = 10001 | (10001 >> 1),即i = 10001|01000=11001,此時i=11001
i |= (i >> 2),即i = 11001 | (11001 >> 2),即i = 11001|00110=11111,此時i=11111
i |= (i >> 4),即i = 11111 | (11111 >> 4),即i = 11111|00001=11111,此時i=11111
i |= (i >> 8),即i = 11111 | (11111 >> 8),即i = 11111|00000=11111,此時i=11111
i |= (i >> 16),即i = 11111 | (11111 >> 16),即i = 11111|00000=11111,此時i=11111
i - (i >>> 1),即11111-01111=10000,10進制即是16
HaspMap中的roundUpToPowerOf2方法,傳入給highestOneBit的是參數number減1的值,爲何需要減1呢?
當參數number的二進制位中不止一個位是1時,此時將該值減1不影響最高位爲1的值,因此減1後的值左移1位後最高位爲1的就有了兩個連續1,調用highestOneBit方法後將最高位的1後面所有值設置爲0。得到的值就是符合要求的值。
當參數number的二進制位中只有一個位是1時,那麼該值肯定是2的n次冪。其實直接返回該值就好了,此處減1後左移1位,再調用highestOneBit實現的效果即是得到number值。
好了,繼續研究HashMap源代碼:
哈希碼計算和哈希表的定位
//int方法是空實現
void init() {
}
//初始化hashSeed的值
final boolean initHashSeedAsNeeded(int capacity) {
boolean currentAltHashing = hashSeed != 0;
boolean useAltHashing = sun.misc.VM.isBooted() &&
(capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
boolean switching = currentAltHashing ^ useAltHashing;
if (switching) {
hashSeed = useAltHashing
? sun.misc.Hashing.randomHashSeed(this)
: 0;
}
return switching;
}
//這斷代碼計算鍵的哈希碼
//如果鍵是字符串,會調用特定的方法去計算字符串的哈希碼
final int hash(Object k) {
int h = hashSeed;
if (0 != h && k instanceof String) {
return sun.misc.Hashing.stringHash32((String) k);
}
h ^= k.hashCode();
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
//通過給定的哈希碼h和哈希表長度length計算該哈希碼所在表的位置
//其中length必須是2的n次冪,這樣可以更快定位
static int indexFor(int h, int length) {
//等價於h%length
return h & (length-1);
}
//返回哈希表鍵值對數量
public int size() {
return size;
}
//返回哈希表是否爲空
public boolean isEmpty() {
return size == 0;
}
get和contains相關方法
//返回鍵key對應的值value,若不存在返回null
public V get(Object key) {
if (key == null)
//key爲null直接調用了getForNullKey方法
return getForNullKey();
//通過鍵key獲得鍵值對
Entry<K,V> entry = getEntry(key);
return null == entry ? null : entry.getValue();
}
//處理鍵key爲null的情況
private V getForNullKey() {
if (size == 0) {
return null;
}
//查找哈希表索引0處的鏈表,可以看出key爲null的鍵值對一定存儲在哈希表的索引0上
for (Entry<K,V> e = table[0]; e != null; e = e.next) {
if (e.key == null)
return e.value;
}
return null;
}
//返回鍵key是否存在
public boolean containsKey(Object key) {
return getEntry(key) != null;
}
//返回鍵key對應的鍵值對,若不存在返回null
final Entry<K,V> getEntry(Object key) {
if (size == 0) {
return null;
}
//計算鍵key的哈希碼
int hash = (key == null) ? 0 : hash(key);
//首先根據哈希碼定位到哈希表中的索引,若鍵key存在則一定存在於該索引對應的鏈表中
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
//比較Entry的hash值和鍵key的哈希值是否相等、比較當前鍵值對的鍵是否和key相等
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
}
return null;
}
put相關方法
//將鍵key、值value添加到哈希表中
//返回之前key關聯的值,若之前key不存在則返回null
public V put(K key, V value) {
//因爲初始化時的哈希表是一個空的哈希表,當需要的時候就要擴容
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
if (key == null)
//鍵key爲null時走putForNullKey方法
return putForNullKey(value);
//取鍵key的哈希碼並定位到哈希表中的索引
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))) {
//此處說明鍵key已經存在,用新值value代替舊值oldValue
V oldValue = e.value;
e.value = value;
//記錄本次操作,這裏的recordAccess其實是個空實現
e.recordAccess(this);
return oldValue;
}
}
//每次對哈希表的結構性改變時都會遞增modCount的值
modCount++;
//鍵key不存在,添加新的鍵值對到哈希表中,鍵值對的hash屬性就是key的哈希碼值
addEntry(hash, key, value, i);
//因爲鍵值對不存在,所以返回null
return null;
}
//鍵爲null情況下的put操作
private V putForNullKey(V value) {
//始終將key爲null的鍵值對添加到哈希表table的索引0處
for (Entry<K,V> e = table[0]; e != null; e = e.next) {
if (e.key == null) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(0, null, value, 0);
return null;
}
//添加鍵值對,該私有方法不會對哈希表進行擴容,並且該方法不會記錄modCount
//該方法僅在通過另一個哈希表來創建哈希表的時候使用
private void putForCreate(K key, V value) {
int hash = null == key ? 0 : hash(key);
int i = indexFor(hash, table.length);
//查找之前已經存在的鍵爲key的鍵值對,並將用新的value代替舊的value
//若沒有找到,創建一個新的鍵值對到哈希表中
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {
e.value = value;
return;
}
}
//新建一個新的鍵值對
createEntry(hash, key, value, i);
}
//該方法很簡單,將哈希表m中的鍵值對添加到本哈希表中
private void putAllForCreate(Map<? extends K, ? extends V> m) {
for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
putForCreate(e.getKey(), e.getValue());
}
重哈希: resize和transfer方法
//當哈希表鍵值對數量達到threshold的時候自動重哈希,重哈希後的哈希表容量變得更大
//newCapacity是重哈希後新的容量
void resize(int newCapacity) {
Entry[] oldTable = table;
int oldCapacity = oldTable.length;
//哈希表的最大容量是MAXIMUN_CAPACITY,達到該值後不會再重哈希
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);
}
//將老的哈希表中所有的鍵值對遷移到新的哈希表中
void transfer(Entry[] newTable, boolean rehash) {
int newCapacity = newTable.length;
//遍歷老的哈希表
for (Entry<K,V> e : table) {
//哈希表的每一個元素都是一個鏈表,遍歷該鏈表
while(null != e) {
Entry<K,V> next = e.next;
//如果需要重新計算鍵的哈希值
if (rehash) {
e.hash = null == e.key ? 0 : hash(e.key);
}
//計算鍵在新的哈希表中的索引
int i = indexFor(e.hash, newCapacity);
//這三行代碼完成了鍵值對的遷移
e.next = newTable[i];
newTable[i] = e;
e = next;
}
}
}
上面代碼片斷功能是遷移老的哈希表所有的鍵值對到新的哈希表,核心思想是就是上面最底行的三行代碼,我們分析下,假設老的哈希表如下圖示:
老的哈希表的容量爲4,假設擴容後的哈希表容量爲8,並且e0重哈希後到新哈希表的索引3處:
可以看到,這三行代碼執行完成後就將e0插入到新哈希表的索引3處,並且處於索引3處鏈表的頭。
繼續看源代碼:
//該方法將哈希表m的所有鍵對加入到本哈希表中
public void putAll(Map<? extends K, ? extends V> m) {
int numKeysToBeAdded = m.size();
if (numKeysToBeAdded == 0)
return;
if (table == EMPTY_TABLE) {
inflateTable((int) Math.max(numKeysToBeAdded * loadFactor, threshold));
}
//計算是否要擴容、重哈希
if (numKeysToBeAdded > threshold) {
int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1);
if (targetCapacity > MAXIMUM_CAPACITY)
targetCapacity = MAXIMUM_CAPACITY;
int newCapacity = table.length;
while (newCapacity < targetCapacity)
newCapacity <<= 1;
if (newCapacity > table.length)
resize(newCapacity);
}
//將哈希表m的所有鍵值對添加到本哈希表中
for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
put(e.getKey(), e.getValue());
}
remove相關方法
//刪除鍵key對應的鍵值對
//返回之前key關聯的值,如果不存在,返回null
public V remove(Object key) {
//其實是調用removeEntryForKey方法刪除的
Entry<K,V> e = removeEntryForKey(key);
return (e == null ? null : e.value);
}
//刪除鍵key對應的鍵值對
final Entry<K,V> removeEntryForKey(Object key) {
if (size == 0) {
return null;
}
int hash = (key == null) ? 0 : hash(key);
int i = indexFor(hash, table.length);
Entry<K,V> prev = table[i];
Entry<K,V> e = prev;
while (e != null) {
Entry<K,V> next = e.next;
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {
//找到了鍵爲key的鍵值對,執行刪除操作
//這裏其實就是執行鏈表的刪除操作,修改相關的指針即可
modCount++;
size--;
//要刪除的結點就是第一個結點
if (prev == e)
table[i] = next;
else
prev.next = next;
e.recordRemoval(this);
return e;
}
prev = e;
e = next;
}
return e;
}
//刪除指定的鍵值對,這裏的o必須是一個Map.Entry
final Entry<K,V> removeMapping(Object o) {
if (size == 0 || !(o instanceof Map.Entry))
return null;
Map.Entry<K,V> entry = (Map.Entry<K,V>) o;
Object key = entry.getKey();
int hash = (key == null) ? 0 : hash(key);
int i = indexFor(hash, table.length);
Entry<K,V> prev = table[i];
Entry<K,V> e = prev;
while (e != null) {
Entry<K,V> next = e.next;
//鍵和值都相等時纔會刪除
if (e.hash == hash && e.equals(entry)) {
modCount++;
size--;
if (prev == e)
table[i] = next;
else
prev.next = next;
e.recordRemoval(this);
return e;
}
prev = e;
e = next;
}
return e;
}
//清空哈希表
public void clear() {
modCount++;
//將table每個值設爲null
Arrays.fill(table, null);
size = 0;
}
//返回哈希表中是否包含值value
public boolean containsValue(Object value) {
if (value == null)
//是否有空的值
return containsNullValue();
Entry[] tab = table;
for (int i = 0; i < tab.length ; i++)
for (Entry e = tab[i] ; e != null ; e = e.next)
if (value.equals(e.value))
return true;
return false;
}
//返回哈希表中是否包含null值
private boolean containsNullValue() {
Entry[] tab = table;
for (int i = 0; i < tab.length ; i++)
for (Entry e = tab[i] ; e != null ; e = e.next)
if (e.value == null)
return true;
return false;
}
clone-淺複製
//克隆方法,實現了淺複製
public Object clone() {
HashMap<K,V> result = null;
try {
//調用父類的clone方法
result = (HashMap<K,V>)super.clone();
} catch (CloneNotSupportedException e) {
// assert false;
}
if (result.table != EMPTY_TABLE) {
result.inflateTable(Math.min(
(int) Math.min(
size * Math.min(1 / loadFactor, 4.0f),
// we have limits...
HashMap.MAXIMUM_CAPACITY),
table.length));
}
result.entrySet = null;
result.modCount = 0;
result.size = 0;
result.init();
//這裏將鍵值對添加到result中
result.putAllForCreate(this);
return result;
}
HashMap.Entry靜態內部類
//鍵值對,實現了Map.Entry接口
//這裏的Entry其實是一個鏈表結點,每個結點有鍵、值、next指針、哈希碼
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next;
int hash;
Entry(int h, K k, V v, Entry<K,V> n) {
value = v;
next = n;
key = k;
hash = h;
}
public final K getKey() {
return key;
}
public final V getValue() {
return value;
}
public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
//equals方法比較了鍵是否相等、值是否相等,只有鍵和值都相等時兩個Entry才相等
public final boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry e = (Map.Entry)o;
Object k1 = getKey();
Object k2 = e.getKey();
if (k1 == k2 || (k1 != null && k1.equals(k2))) {
Object v1 = getValue();
Object v2 = e.getValue();
if (v1 == v2 || (v1 != null && v1.equals(v2)))
return true;
}
return false;
}
//鍵值對的哈希碼,需要和hash屬性區別開來,這裏的hash存儲的是鍵的哈希碼
public final int hashCode() {
return Objects.hashCode(getKey()) ^ Objects.hashCode(getValue());
}
public final String toString() {
return getKey() + "=" + getValue();
}
void recordAccess(HashMap<K,V> m) {
}
void recordRemoval(HashMap<K,V> m) {
}
}
addEntry方法
//增加鍵值對、需要考慮重哈希
void addEntry(int hash, K key, V value, int bucketIndex) {
if ((size >= threshold) && (null != table[bucketIndex])) {
//新哈希表的容量擴充爲原來的2倍
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++;
}
HashMap內部迭代器以及迭代器快速失敗機制
//HashMap使用到的迭代器的抽象基類
private abstract class HashIterator<E> implements Iterator<E> {
Entry<K,V> next;
//通過expectedModCount實現迭代器的快速失敗機制
int expectedModCount;
//迭代器當前所在的哈希表索引
int index;
Entry<K,V> current;
HashIterator() {
//expectedModCount初始化爲HashMap的成員變量modCount
expectedModCount = modCount;
if (size > 0) {
//找到第一個有值的鏈表
Entry[] t = table;
while (index < t.length && (next = t[index++]) == null)
;
}
}
public final boolean hasNext() {
return next != null;
}
final Entry<K,V> nextEntry() {
//說明在迭代器迭代的時候HashMap已經被結構性改變,拋出併發修改異常
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
Entry<K,V> e = next;
if (e == null)
throw new NoSuchElementException();
//返回Entry前,需要準備好下一次迭代的Entry
if ((next = e.next) == null) {
Entry[] t = table;
while (index < t.length && (next = t[index++]) == null)
;
}
current = e;
return e;
}
//刪除操作,調用HashMap.removeEntryForKey方法實現的
public void remove() {
//還沒有開始遍歷、或者沒有任何節點、或者已經刪除,current爲空
if (current == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
Object k = current.key;
//刪除後將current設置爲空,防止重複刪除
current = null;
HashMap.this.removeEntryForKey(k);
//刪除後更新expectedModCount的值,不然會拋出ConcurrentModificationException
expectedModCount = modCount;
}
}
//值集合的迭代器,靜態內部類
private final class ValueIterator extends HashIterator<V> {
public V next() {
return nextEntry().value;
}
}
//鍵集合的迭代器,靜態內部類
private final class KeyIterator extends HashIterator<K> {
public K next() {
return nextEntry().getKey();
}
}
//鍵值對的迭代器,靜態內部類
private final class EntryIterator extends HashIterator<Map.Entry<K,V>> {
public Map.Entry<K,V> next() {
return nextEntry();
}
}
//返回一個鍵集合的迭代器
Iterator<K> newKeyIterator() {
return new KeyIterator();
}
//返回一個值集合的迭代器
Iterator<V> newValueIterator() {
return new ValueIterator();
}
//返回一個鍵值對集合的迭代器
Iterator<Map.Entry<K,V>> newEntryIterator() {
return new EntryIterator();
}
鍵、值、鍵值對集合
//返回鍵的Set集合
public Set<K> keySet() {
Set<K> ks = keySet;
return (ks != null ? ks : (keySet = new KeySet()));
}
//鍵的集合,繼承自AbstractSet的內部類
private final class KeySet extends AbstractSet<K> {
//返回一個鍵的迭代器
public Iterator<K> iterator() {
return newKeyIterator();
}
//鍵的數量
public int size() {
return size;
}
//是否存在鍵o
public boolean contains(Object o) {
return containsKey(o);
}
//刪除鍵o對應的鍵值對
public boolean remove(Object o) {
return HashMap.this.removeEntryForKey(o) != null;
}
//清空
public void clear() {
HashMap.this.clear();
}
}
//返回值的集合
public Collection<V> values() {
Collection<V> vs = values;
return (vs != null ? vs : (values = new Values()));
}
//值的集合,繼承自AbstractCollection的內部類
private final class Values extends AbstractCollection<V> {
//返回一個值的迭代器
public Iterator<V> iterator() {
return newValueIterator();
}
public int size() {
return size;
}
public boolean contains(Object o) {
return containsValue(o);
}
public void clear() {
HashMap.this.clear();
}
}
//返回鍵值對的Set集合
public Set<Map.Entry<K,V>> entrySet() {
return entrySet0();
}
private Set<Map.Entry<K,V>> entrySet0() {
Set<Map.Entry<K,V>> es = entrySet;
return es != null ? es : (entrySet = new EntrySet());
}
//鍵值對的集合,繼承自AbstractSet的內部類
private final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
//返回鍵值對集合的迭代器
public Iterator<Map.Entry<K,V>> iterator() {
return newEntryIterator();
}
public boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<K,V> e = (Map.Entry<K,V>) o;
Entry<K,V> candidate = getEntry(e.getKey());
return candidate != null && candidate.equals(e);
}
public boolean remove(Object o) {
return removeMapping(o) != null;
}
public int size() {
return size;
}
public void clear() {
HashMap.this.clear();
}
}
writeObject和readObject序列化相關方法
另外,HashMap也實現了writeObject和readObject方法,實現自定義的序列化:
//序列化方法
private void writeObject(java.io.ObjectOutputStream s)
throws IOException
{
//默認序列化
s.defaultWriteObject();
//序列化哈希表的大小
if (table==EMPTY_TABLE) {
s.writeInt(roundUpToPowerOf2(threshold));
} else {
s.writeInt(table.length);
}
//序列化哈希表鍵值對數量
s.writeInt(size);
//序列化鍵值對
if (size > 0) {
for(Map.Entry<K,V> e : entrySet0()) {
s.writeObject(e.getKey());
s.writeObject(e.getValue());
}
}
}
//反序列化方法
private void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException
{
// 默認反序列化
s.defaultReadObject();
if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
throw new InvalidObjectException("Illegal load factor: " +
loadFactor);
}
table = (Entry<K,V>[]) EMPTY_TABLE;
// 反序列化哈希表的大小
s.readInt();
// 反序列化哈希表鍵值對數量
int mappings = s.readInt();
if (mappings < 0)
throw new InvalidObjectException("Illegal mappings count: " +
mappings);
int capacity = (int) Math.min(
mappings * Math.min(1 / loadFactor, 4.0f),
HashMap.MAXIMUM_CAPACITY);
if (mappings > 0) {
inflateTable(capacity);
} else {
threshold = capacity;
}
init();
//反序列化鍵值對
for (int i = 0; i < mappings; i++) {
K key = (K) s.readObject();
V value = (V) s.readObject();
putForCreate(key, value);
}
}
//返回哈希表的容量,也就是table數組的大小
int capacity() { return table.length; }
//返回加載因子
float loadFactor() { return loadFactor; }
到此爲止,HashMap源碼分析完了
參考:
- jdk源碼