繼承樹
注意下方的"元素"二字
按照習慣,先看建構函式和第一次新增
首先無參的建構函式
讓過載因子等於0.75
public hashmap()
然後是put函式,呼叫了內部的putval函式
public v put(k key, v value)
在看這個方法之前,要先知道hashmap中儲存元素的型別
內部類node
首先,hashmap的鍵值對,儲存在內部類node中
它內部有四個屬性
final int hash;
final k key;
v value;
nodenext;
hash用來儲存hash值,next是處理hash衝突用的
方法有:
四個引數的構造器
key和value的getter
tostring
hashcode
setvalue
equals
**table
儲存資料的實際陣列就是node table
putval方法,通過注釋解釋
大致邏輯就是,如果占用的雜湊位址已經超過了負載容量,並且新插入元素的負載位址已經有元素了,那麼開啟擴容
當onlyifabsent為false時:
在對應的雜湊位址中尋找有沒有key相同的元素,如果有,相應的value取而代之,返回舊的value
如果沒有,將元素插入鍊錶末尾,如果鍊錶長度在插入後大於等於8,則將鍊錶變為樹型結構
當onlyifabsent為true時:
找到了key相同的元素就直接返回,不會替換
final v putval(int hash, k key, v value, boolean onlyifabsent,
boolean evict)
//如果找到了key相同的
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;}}
v oldvalue = e.value;
if (!onlyifabsent || oldvalue == null)
e.value = value;
//linkedhashmap留用
afternodeaccess(e);
return oldvalue;}}
//沒有找到key值是一樣的,標記進行了增刪操作
++modcount;
//如果已經利用的table中雜湊位址的數量超過了負載容量,則擴容
//也就是說,如果沒有找到
if (++size > threshold)
resize();
//linkedhashmap留用
afternodeinsertion(evict);
return null;
}
讓我們在**一下putval呼叫的方法
resize()
主要邏輯就是先計算出新的容量(*2)和新的負載容量,然後將原來的元素重新插入到雜湊表中
final node resize()
else if ((newcap = oldcap << 1) < maximum_capacity &&
oldcap >= default_initial_capacity)
//新的負載容量變為2倍
newthr = oldthr << 1; // double threshold
}//如果原來的負載容量大於0
else if (oldthr > 0) // initial capacity was placed in threshold
newcap = oldthr;
else
if (newthr == 0)
threshold = newthr;
@suppresswarnings()
//建立陣列,重新插入
node newtab = (node)new node[newcap];
table = newtab;
if (oldtab != null)
else
} while ((e = next) != null);
//把low和high放入相應的雜湊位置中去
if (lotail != null)
if (hitail != null) }}
}}return newtab;
}
final void treeifybin(node tab, int hash)
tl = p;
} while ((e = e.next) != null);
//真正地構建一顆紅黑樹
//這裡先挖個坑,以後再來看吧
if ((tab[index] = hd) != null)
hd.treeify(tab);}}
雖然我沒有確切地分析treeify方法,但是還是說一下內部大致邏輯:
1.如果樹裡面還什麼都沒有,那就將插入的點作為root
2.把這顆樹當做普通搜尋二叉樹,找到新節點應該插入的位置,並將其插入
3.進行必要的平衡操作:balanceinsertion()
方法
在比較時,將hash值作為key和判斷大小的依據
當hash值相等時,用到了comparableclassfor()
和comparecomparables()
方法來操作
第乙個方法是返回能夠使用comparable介面來判斷大小,如果能,則使用第二個方法,如果不能或者判斷結果仍然是相等,則使用tiebreakorder()
方法進行判斷
這就是乙個基本的插入流程了.
讓我們回頭看一下一些注意點
構造器
無參構造器,負載因子指定為預設值
public hashmap()
public hashmap(int initialcapacity, float loadfactor)
指定容量,負載因子指定為預設值
public hashmap(int initialcapacity)
讓我們來分析一下這個方法
//這是在幹啥qaq
//實際上就是把從最高位的1開始
//將低位全部置為1
//讓後加1返回.
//將cap寫成二進位制數,手動模擬一下就可以看得很清楚了
//因為1+2+4+8+16 = 31
//所以剛好處理int正數
static final int tablesizefor(int cap)
key與value集合hashmap提供了keyset()
方法,返回乙個set包含了所有key值
觀察這個方法可以發現,如果hashmap的成員變數keyset不為空,那麼直接放回,否則生成乙個keyset
但是這個類何時更新,怎麼賦值,我都沒找到原始碼,說是詭異一點都不過分
values()
方法也一樣,hashmap還提供了entryset()
方法,都是大同小異的
hash
眾所周知,hash是將乙個物件對映為乙個正整數,物件千變萬化而hash值範圍有限,所以可能出現多個物件對應乙個正整數的情況
在object中,有public native int hashcode();
這樣乙個方法,它的實現我們不知道是什麼,但是可以猜測,因為定義在object內,應該與物件的屬性無關,而與型別,位址等可能有關.
string等類重寫了hashcode方法,如果自定義類也想放入hashmap也需要重寫hashcode方法,當然還有equals方法.但是由於這兩方法都定義在object中,所以不寫也不會報錯
hashmap中,不僅僅是簡單地呼叫了物件的hashcode方法
比如
static final int hash(object key)
這個方法在public v put(k key, v value)
中被呼叫了
它實際上是將key的hashval處理了一下
但是為什麼要這麼處理呢?
這是一種hash擾動,如果只有高位不等,低位相等時,hash&(length-1),當length<2^16時就會相等,為了避免這種情況,於是高位與低位異或,使得低位與高位相關聯.
那麼能讓本來低位一樣的兩個hash值變得低位不一樣,當然也是有可能讓本來兩個低位不等的hash值變得低位相等的
但是hashcode這麼寫仍然很有必要,因為key可能是自定義的,你自己重寫的hashcode方法可能有失水準
多嘴一句,在hashmap中,對陣列長度取模,用的是&運算哦
HashMap原始碼閱讀
the default initial capacity must be a power of two.預設容量必須是2的n次方,預設大小為16 static final int default initial capacity 16 the maximum capacity,used if a h...
原始碼閱讀 HashMap
資料結構 jdk1.8對hashmap進行了比較大的優化,底層由以前的陣列 鍊錶變成了陣列 鍊錶 紅黑樹的實現形式,當鏈結結點較少時用鍊錶,當鏈結結點超過一定值的時候用紅黑樹。繼承實現 屬性 預設容量 static final int default initial capacity 16 最大容量...
HashMap 1 8 原始碼閱讀
一 初始化 1.無參建構函式 負載因子預設值 static final float default load factor 0.75f 指定loadfactor負載因子的值是0.75f public hashmap 2.指定初始化大小和負載因子 hashmap的最大容量 static final i...