Java 集合框架分析 - HashMap
本篇文章主要分析一下Java集合框架中的Map部分,HashMap,該源碼分析基于JDK1.8,分析工具,AndroidStudio,文章分析不足之處,還請見諒!
一、HashMap簡介
基于哈希表的一個Map接口實現,存儲的對象是一個鍵值對對象 (Entry<K,V>) ;值得注意的是HashMap不是線程安全的,如果想要線程安全的HashMap,可以通過Collections類的靜態方法synchronizedMap獲得線程安全的HashMap。
Map map = Collections.synchronizedMap(new HashMap());
二、數據結構
Java最基本的數據結構有數組和鏈表。數組的特點是空間連續(大小固定)、尋址迅速,但是插入和刪除時需要移動元素,所以查詢快,增加刪除慢。鏈表恰好相反,可動態增加或減少空間以適應新增和刪除元素,但查找時只能順著一個個節點查找,所以增加刪除快,查找慢。有沒有一種結構綜合了數組和鏈表的優點呢?當然有,那就是哈希表(雖說是綜合優點,但實際上查找肯定沒有數組快,插入刪除沒有鏈表快,一種折中的方式吧),所有的數據結構都可以用這兩個基本結構來構造的,HashMap也不例外。HashMap實際上是一個“鏈表散列”的數據結構,即數組和鏈表的結合體。
HashMap的底層主要是基于數組和鏈表來實現的,它之所以有相當快的查詢速度主要是因為它是通過計算散列碼來決定存儲的位置。HashMap中主要是通過key的hashCode來計算hash值的,只要hashCode相同,計算出來的hash值就一樣。如果存儲的對象對多了,就有可能不同的對象所算出來的hash值是相同的,這就出現了所謂的hash沖突。學過數據結構的同學都知道,解決hash沖突的方法有很多,HashMap底層是通過鏈表來解決hash沖突的。
圖中,左邊部分即代表哈希表,也稱為哈希數組,數組的每個元素都是一個單鏈表的頭節點,鏈表是用來解決沖突的,如果不同的key映射到了數組的同一位置處,就將其放入單鏈表中。
三、內部接口Entry
Entry接口是Map定義的一個內部接口
interface Entry<K, V> {
K getKey();
V getValue();
V setValue(V value);
boolean equals(Object o);
int hashCode();
public static <K extends Comparable<? super K>, V> Comparator<Map.Entry<K, V>> comparingByKey() {
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> c1.getKey().compareTo(c2.getKey());
}
public static <K, V extends Comparable<? super V>> Comparator<Map.Entry<K, V>> comparingByValue() {
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> c1.getValue().compareTo(c2.getValue());
}
public static <K, V> Comparator<Map.Entry<K, V>> comparingByKey(Comparator<? super K> cmp) {
Objects.requireNonNull(cmp);
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> cmp.compare(c1.getKey(), c2.getKey());
}
public static <K, V> Comparator<Map.Entry<K, V>> comparingByValue(Comparator<? super V> cmp) {
Objects.requireNonNull(cmp);
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> cmp.compare(c1.getValue(), c2.getValue());
}
}
對于HashMap,它的內部里面實現一個靜態內部類,即為 HashMapEntry<K,V> ,其重要的屬性有 key , value, next,從屬性key,value我們就能很明顯的看出來HashMapEntry就是HashMap鍵值對實現的一個基礎bean,我們上面說到HashMap的基礎就是一個線性數組,這個數組就是HashMapEntry[],Map里面的內容都保存在HashMapEntry[]里面。
/**
* The table, resized as necessary. Length MUST Always be a power of two.
*/
transient HashMapEntry<K,V>[] table = (HashMapEntry<K,V>[]) EMPTY_TABLE;
我們可以來簡單分析一下這個靜態內部類HashMapEntry.java
// HashMapEntry.java靜態內部類,實現的HashMap線性數組
static class HashMapEntry<K,V> implements Map.Entry<K,V> {
//key,value值
final K key;
V value;
//每個數組里面包含的鏈表
HashMapEntry<K,V> next;
int hash;
/**
* 構造函數
* 輸入參數包括"哈希值(h)", "鍵(k)", "值(v)", "下一節點(n)"
*/
HashMapEntry(int h, K k, V v, HashMapEntry<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;
}
/**
* 判斷兩個Entry是否相等
* 若兩個Entry的“key”和“value”都相等,則返回true。
* 否則,返回false
*/
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;
}
public final int hashCode() {
return Objects.hashCode(getKey()) ^ Objects.hashCode(getValue());
}
public final String toString() {
return getKey() + "=" + getValue();
}
/**
* 當向HashMap中添加元素時,會調用recordAccess()
*/
void recordAccess(HashMap<K,V> m) {
}
/**
* 當從HashMap中刪除元素時,會調用recordRemoval()。
*/
void recordRemoval(HashMap<K,V> m) {
}
}
HashMap其實就是一個HashMapEntry數組,HashMapEntry對象中包含了鍵和值,其中next也是一個HashMapEntry對象,它就是用來處理hash沖突的,形成一個鏈表。
基本結構我們已經分析了,接下來我們就開始正題,開始分析HashMap的源碼實現啦!
四、源碼分析
4.1 類申明
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable
首先我們來看看HashMap內部申明的一些屬性,
//屬性申明
//默認的初始容量,必須是2的冪次方。
static final int DEFAULT_INITIAL_CAPACITY = 4;
//最大容量,默認為2的30次方,
static final int MAXIMUM_CAPACITY = 1 << 30;
//加載因子
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//
static final HashMapEntry<?,?>[] EMPTY_TABLE = {};
//數組表,大小可以改變,且大小必須為2的冪
transient HashMapEntry<K,V>[] table = (HashMapEntry<K,V>[]) EMPTY_TABLE;
//當前Map中key-value映射的個數
transient int size;
//當實際大小超過臨界值時,會進行擴容threshold = 加載因子*容量
int threshold;
//加載因子
final float loadFactor = DEFAULT_LOAD_FACTOR;
//Hash表結構性修改次數
transient int modCount;
對于加載因子loadFactor,我們可以稍作解釋:
loadFactor加載因子是表示Hsah表中元素的填滿的程度.
若:加載因子越大,填滿的元素越多,好處是,空間利用率高了,但:沖突的機會加大了.鏈表長度會越來越長,查找效率降低。反之,加載因子越小,填滿的元素越少,好處是:沖突的機會減小了,但:空間浪費多了.表中的數據將過于稀疏(很多空間還沒用,就開始擴容了)沖突的機會越大,則查找的成本越高.因此,必須在
"沖突的機會"與"空間利用率"之間尋找一種平衡與折衷. 這種平衡與折衷本質上是數據結構中有名的"時-空"矛盾的平衡與折衷.
如果機器內存足夠,并且想要提高查詢速度的話可以將加載因子設置小一點;相反如果機器內存緊張,并且對查詢速度沒有什么要求的話可以將加載因子設置大一點。不過一般我們都不用去設置它,讓它取默認值0.75就好了。
以上一些基本屬性是在HashMap中申明的,如果不明白什么意思的話,可以等到后面再來看看。我們接著分析下面的代碼。
4.2 構造函數
接著我們來分析分析HashMap的構造函數
/**
* 設置初始容量大小以及加載因子
*
* @param initialCapacity the initial capacity
* @param loadFactor the load factor
* @throws IllegalArgumentException if the initial capacity is negative
* or the load factor is nonpositive
*/
public HashMap(int initialCapacity, float loadFactor) {
//初始容量大小在[DEFAULT_INITIAL_CAPACITY,MAXIMUM_CAPACITY]之間
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY) {
initialCapacity = MAXIMUM_CAPACITY;
} else if (initialCapacity < DEFAULT_INITIAL_CAPACITY) {
initialCapacity = DEFAULT_INITIAL_CAPACITY;
}
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
threshold = initialCapacity;
init();
}
/**
* 設置初始容量,加載因子為默認的0.75
* @param initialCapacity the initial capacity.
* @throws IllegalArgumentException if the initial capacity is negative.
*/
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
/**
* 使用默認的容量大小和加載因子
*/
public HashMap() {
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}
/**
* 根據指定的map生成一個新的HashMap,負載因子使用默認值,初始容量大小為Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,DEFAULT_INITIAL_CAPACITY)
*
* @param m the map whose mappings are to be placed in this map
* @throws NullPointerException if the specified map is null
*/
public HashMap(Map<? extends K, ? extends V> m) {
this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
//初始化HashMapEntry數組
inflateTable(threshold);
putAllForCreate(m);
}
通過以上HashMap的四種構造函數我們可以發現,我們可以通過設置初始容量大小和加載因子或者直接通過一個map來構造一個HashMap,其中初始容量大小我們設定為4,它必須是2的冪次方,同時它的范圍在4和2的30次方之間。構造函數比較簡單,沒什么可以分析的,接下來我們著重分析一下HashMap的添加和獲取方法,也就是put和get方法。
4.3 存儲數據put
//向map存入一個鍵值對,如果key已存在,則覆蓋
public V put(K key, V value) {
//如果HashMapEntry數組為空,則重新初始化一下
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
//對key為null的鍵值對調用putForNullKey處理
if (key == null)
return putForNullKey(value);
//生成hash值
int hash = sun.misc.Hashing.singleWordWangJenkinsHash(key);
//生成hash值索引
int i = indexFor(hash, table.length);
//循環遍歷Entry數組,若“該key”對應的鍵值對已經存在,則用新的value取代舊的value。然后退出,同時返回舊的value!
for (HashMapEntry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
//如果找到了相同的key,則覆蓋舊的value
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
//調用HashMapEntry增加這個Entry
e.recordAccess(this);
return oldValue;
}
}
//修改次數
modCount++;
//將key-value添加到table[i]處,也就是在鏈表的頭部插入一個數據<k,v>
addEntry(hash, key, value, i);
return null;
}
以上的大概內容就是,先獲取key的hash值,然后根據hash值來獲取在數組中的位置,判斷在table[i]中是否存在這個key,也就是先循環遍歷鏈表,如果找到這個key的值,那么就替換這個key值,然后將value修改進去,如果在鏈表中找不到這個key的話,那么就在鏈表的頭部插入一個數據。
在put方法中,我們來詳細分析其中的一些方法。開頭有一個判斷數組是否為null的操作
//如果HashMapEntry數組為空,則重新初始化一下
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
/**
* 初始化這個數組
*/
private void inflateTable(int toSize) {
// 尋找大于toSize的2的冪次方的最小值
int capacity = roundUpToPowerOf2(toSize);
float thresholdFloat = capacity * loadFactor;
if (thresholdFloat > MAXIMUM_CAPACITY + 1) {
thresholdFloat = MAXIMUM_CAPACITY + 1;
}
//初始化數組
threshold = (int) thresholdFloat;
table = new HashMapEntry[capacity];
}
接下來,判斷了key是否為null,如果為null的話,則進行 putForNullKey(V value) 操作
/**
* 插入key為null的值,在數組的第一個位置進行插入操作
*/
private V putForNullKey(V value) {
//循環遍歷數組第一個數組的鏈表,如果存在null的則進行覆蓋更新操作
for (HashMapEntry<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++;
//鏈表中不存在key為null的值,則在鏈表的開頭插入這個key為null的值
addEntry(0, null, value, 0);
return null;
}
如果key為null的話,hash值為0,對象存儲在數組中索引為0的位置。即table[0],接著我們分析一下addEntry方法,
//通過hash值來算出在table中哪個位置,然后進行插入操作
void addEntry(int hash, K key, V value, int bucketIndex) {
//
if ((size >= threshold) && (null != table[bucketIndex])) {
//數組擴容
resize(2 * table.length);
//如果key為null的話,那么hash值為0,也就是在數組的第一個位置,即table[0]位置
hash = (null != key) ? sun.misc.Hashing.singleWordWangJenkinsHash(key) : 0;
//根據hash的值來算出在數組的中位置
bucketIndex = indexFor(hash, table.length);
}
//數據插入或者更新操作
createEntry(hash, key, value, bucketIndex);
}
我們通過hash值計算得到了table下表中的值,接著繼續分析createEntry方法
//數據操作,在鏈表頭部插入一個數組,這就是在一個鏈表頭部插入一個節點的過程。獲取table[i]的對象e,將table[i]的對象修改為新增對象,讓新增對象的next指向e。
void createEntry(int hash, K key, V value, int bucketIndex) {
//保存table[i]的對象為e
HashMapEntry<K,V> e = table[bucketIndex];
//然后將table[i]的值修改為新增的對象,并將新增對象的next值設置為原先table[i]的值e,這就相當于在鏈表頭部插入一個數據了。
table[bucketIndex] = new HashMapEntry<>(hash, key, value, e);
size++;
}
分析完了key為null的情況,接著我們分析一下key不等于null的情況。接著,我們通過key來計算出hash的,然后根據hash的值來計算在table數組中的位置 int i = indexFor(hash, table.length);
// indexFor返回hash值和table數組長度減1的與運算結果。為什么使用的是length-1?應為這樣可以保證結果的最大值是length-1,不會產生數組越界問題。
static int indexFor(int h, int length) {
return h & (length-1);
}
我們找到了在數組table中的位置后,我們就開始遍歷當前位置中的鏈表了,如果存在key則覆蓋操作,不存在的話則在鏈表頭部插入一個數據。
上面便是一個完整的增加數據的操作,在這里我們進行概括一下中心思想。
首先,我們判斷我們需要增加數據的key是否為null,如果為null的話,則在數組的第一個位置即table[0]處進行插入或者更新操作(如果在第一個位置處的鏈表中存在key未null的數據,那么則進行覆蓋更新操作,如果不存在的話,則在鏈表頭插入一個數據,就是常見的鏈表插入操作),接下來就走key不等于null這一步了,我們需要根據key來計算出hash的值,其次,需要根據hash的值來計算在table中的位置,得到了位置之后,我們重復進行在鏈表中的操作(找到就覆蓋更新,沒找到就在鏈表頭部插入一個數據),這就是HashMap的put操作。
我們一般對哈希表的散列很自然地會想到用hash值對length取模(即除法散列法),Hashtable中也是這樣實現的,這種方法基本能保證元素在哈希表中散列的比較均勻,但取模會用到除法運算,效率很低,HashMap中則通過h&(length-1)的方法來代替取模,同樣實現了均勻的散列,但效率要高很多,這也是HashMap對Hashtable的一個改進。
接下來,我們分析下為什么哈希表的容量一定要是2的整數次冪。首先,length為2的整數次冪的話,h&(length-1)就相當于對length取模,這樣便保證了散列的均勻,同時也提升了效率;其次,length為2的整數次冪的話,為偶數,這樣length-1為奇數,奇數的最后一位是1,這樣便保證了h&(length-1)的最后一位可能為0,也可能為1(這取決于h的值),即與后的結果可能為偶數,也可能為奇數,這樣便可以保證散列的均勻性,而如果length為奇數的話,很明顯length-1為偶數,它的最后一位是0,這樣h&(length-1)的最后一位肯定為0,即只能為偶數,這樣任何hash值都只會被散列到數組的偶數下標位置上,這便浪費了近一半的空間,因此,length取2的整數次冪,是為了使不同hash值發生碰撞的概率較小,這樣就能使元素在哈希表中均勻地散列。
分析完了put方法,接下來我們分析一下putAll方法,
/**
* @param m mappings to be stored in this map
* @throws NullPointerException if the specified map is null
*/
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中的內容,然后調用put方法將元素添加到table數組中
for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
put(e.getKey(), e.getValue());
}
遍歷的時候涉及到了entrySet方法,這個方法定義在Map接口中,HashMap中也有實現。我們在HashMap構造函數有個方法叫putAllForCreate方法,分析一下內容。
private void putAllForCreate(Map<? extends K, ? extends V> m) {
for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
putForCreate(e.getKey(), e.getValue());
}
看這個方法名就知道大概意思,在HashMap初始化的時候將一個map全部賦值給初始值。代碼也是,先遍歷一下Map,然后依次添加進去,我們進入putForCreate方法中查看具體源碼
private void putForCreate(K key, V value) {
//獲取key的hash值
int hash = null == key ? 0 : sun.misc.Hashing.singleWordWangJenkinsHash(key);
//根據hash值來計算在table中的位置
int i = indexFor(hash, table.length);
//循環遍歷數組下標為i的鏈表,如果存在則覆蓋更新,也就是上面計算出來的table中的位置
for (HashMapEntry<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);
}
該方法先計算需要添加的元素的hash值和在table數組中的索引i。接著遍歷table[i]的鏈表,若有元素的key值與傳入key值相等,則替換value,結束方法。若不存在key值相同的元素,則調用createEntry創建并添加元素。繼續進入createEntry方法中查看源碼
void createEntry(int hash, K key, V value, int bucketIndex) {
HashMapEntry<K,V> e = table[bucketIndex];
table[bucketIndex] = new HashMapEntry<>(hash, key, value, e);
size++;
}
該方法比較簡單,就是在鏈表頭部插入一條數據,putAllForCreate方法類似于put方法。至此所有put相關操作都解釋完畢了。put之外,另一個常用的操作就是get,下面就來看get方法。
4.4 get方法
相比較于put方法,get方法還是比較簡單的,我們來具體看下實現。
public V get(Object key) {
if (key == null)
return getForNullKey();
Entry<K,V> entry = getEntry(key);
return null == entry ? null : entry.getValue();
}
首先判斷key是否等于null,等于的話那就從getForNullKey()中獲取value,如果不等于的話,通過getEntry獲取。我們首先看等于null的情況。
private V getForNullKey() {
if (size == 0) {
return null;
}
for (HashMapEntry<K,V> e = table[0]; e != null; e = e.next) {
if (e.key == null)
return e.value;
}
return null;
}
如果當前table的大小為0的話,那么將返回null,如果不為空的話,那么則在數組table[0]中的鏈表進行循環匹配,尋找key的null的鏈表節點,如果找到的話,則直接返回value,否則的話返回null,很簡單。接著我們分析key不等于null的情況,即進入getEntry方法中分析。
final Entry<K,V> getEntry(Object key) {
if (size == 0) {
return null;
}
int hash = (key == null) ? 0 : sun.misc.Hashing.singleWordWangJenkinsHash(key);
for (HashMapEntry<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;
}
我們已經分析了put方法,所以這里面的代碼基本類似,很簡單了,首先先獲取hash值,然后根據hash值獲取在table中的索引table[i],其次根據這個數組中的位置來循環遍歷table[i]中的鏈表,判斷是否存在這個entry如果存在的話則返回value,不存在則返回null。
以上便是HashMap中比較重要的兩個方法,一個put,一個get,我們已經全部分析完了,接著我們分析一下其余剩下來的方法。
4.5 remove方法
分析完了put、get方法,接著分析一下remove方法。
public V remove(Object key) {
Entry<K,V> e = removeEntryForKey(key);
return (e == null ? null : e.getValue());
}
在remove方法中,調用了一個removeEntryForKey方法,我們接著看看,
final Entry<K,V> removeEntryForKey(Object key) {
//如果數組為空,直接返回null
if (size == 0) {
return null;
}
//獲取需要移除的key的hash值
int hash = (key == null) ? 0 : sun.misc.Hashing.singleWordWangJenkinsHash(key);
//獲取在數組table[i]中的索引值i
int i = indexFor(hash, table.length);
保存當前數組中的第一個鏈表節點
HashMapEntry<K,V> prev = table[i];
HashMapEntry<K,V> e = prev;
while (e != null) {
HashMapEntry<K,V> next = e.next;
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {
modCount++;
size--;
if (prev == e)
table[i] = next;
else
prev.next = next;
e.recordRemoval(this);
return e;
}
//單鏈表刪除操作
prev = e;
e = next;
}
return e;
}
上面的這個過程就是先找到table數組中對應的索引,接著就類似于一般的鏈表的刪除操作,而且是單向鏈表刪除節點,很簡單。在C語言中就是修改指針,這個例子中就是將要刪除節點的前一節點的next指向刪除被刪除節點的next即可。
4.6 其余方法
以上分析了HashMap的大部分代碼,還有一些比較不重要的方法我們一次性給出解釋,就不一一做分析了。
/**
* 清空map
*/
public void clear() {
modCount++;
Arrays.fill(table, null);
size = 0;
}
/**
* 判斷是否存在指定的value
*
* @param value value whose presence in this map is to be tested
* @return <tt>true</tt> if this map maps one or more keys to the
* specified value
*/
public boolean containsValue(Object value) {
if (value == null)
return containsNullValue();
HashMapEntry[] tab = table;
for (int i = 0; i < tab.length ; i++)
for (HashMapEntry e = tab[i] ; e != null ; e = e.next)
if (value.equals(e.value))
return true;
return false;
}
/**
* 循環遍歷數組,判斷數組table[i]下面的鏈表是否存在value等于null的節點
*/
private boolean containsNullValue() {
HashMapEntry[] tab = table;
for (int i = 0; i < tab.length ; i++)
for (HashMapEntry e = tab[i] ; e != null ; e = e.next)
if (e.value == null)
return true;
return false;
}
/**
* 針對HashMap的淺復制
*
* @return a shallow copy of this map
*/
public Object clone() {
HashMap<K,V> result = null;
try {
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.putAllForCreate(this);
return result;
}
以上便是HashMap大概的源碼,其中還涉及到entrySet的概念,這是其他集合框架也涉及到的,所以打算一起說,本篇暫未說明。敬請期待后面的文章。如有錯誤,懇請指正,謝謝!
參考鏈接
1、http://blog.csdn.net/vking_wang/article/details/14166593
2、http://www.cnblogs.com/hzmark/archive/2012/12/24/HashMap.html
3、http://www.cnblogs.com/chenpi/p/5280304.html
4、http://www.cnblogs.com/ITtangtang/p/3948406.html
來自:https://juejin.im/entry/58c7ecb3128fe10060498577