HashMap的介紹
1、HashMap的簡介
基于哈希表的 Map 接口的實現(xiàn)捶惜。此實現(xiàn)提供所有可選的映射操作,并允許使用 null 值和 null 鍵榆俺。(除了非同步和允許使用 null 之外售躁,HashMap 類與 Hashtable 大致相同坞淮。)此類不保證映射的順序茴晋,特別是它不保證該順序恒久不變。
2回窘、HashMap的構(gòu)造函數(shù)
//構(gòu)造一個帶指定初始容量和默認加載因子 (0.75) 的空 HashMap
public HashMap(int initialCapacity)
//構(gòu)造一個具有默認初始容量 (16) 和默認加載因子 (0.75) 的空 HashMap
public HashMap()
//構(gòu)造一個映射關(guān)系與指定 Map 相同的新 HashMap
public HashMap(Map<? extends K, ? extends V> m)
//構(gòu)造一個帶指定初始容量和加載因子的空 HashMap
public HashMap(int initialCapacity, float loadFactor)
HashMap的數(shù)據(jù)結(jié)構(gòu)
HashMap的重要字段說明
//創(chuàng)建 HashMap 時未指定初始容量情況下的默認容量
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
//HashMap 的最大容量
static final int MAXIMUM_CAPACITY = 1 << 30;
//HashMap 默認的裝載因子,當 HashMap 中元素數(shù)量超過 容量*裝載因子 時诺擅,進行 resize() 操作
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//用來確定何時將解決 hash 沖突的鏈表轉(zhuǎn)變?yōu)榧t黑樹
static final int TREEIFY_THRESHOLD = 8;
// 用來確定何時將解決 hash 沖突的紅黑樹轉(zhuǎn)變?yōu)殒湵? static final int UNTREEIFY_THRESHOLD = 6;
/* 當需要將解決 hash 沖突的鏈表轉(zhuǎn)變?yōu)榧t黑樹時,需要判斷下此時數(shù)組容量啡直,
若是由于數(shù)組容量太兴赣俊(小于 MIN_TREEIFY_CAPACITY〔缘)導致的 hash 沖突太多,
則不進行鏈表轉(zhuǎn)變?yōu)榧t黑樹操作撮执,轉(zhuǎn)為利用 resize() 函數(shù)對 hashMap 擴容 */
static final int MIN_TREEIFY_CAPACITY = 64;
HashMap的重要方法和源碼解析
HashMap就是一個散列表微峰,它是通過“拉鏈法”解決哈希沖突的,影響HashMap性能的有兩個參數(shù):初始容量(initialCapacity) 和加載因子(loadFactor)抒钱。容量 是哈希表中桶的數(shù)量蜓肆,初始容量只是哈希表在創(chuàng)建時的容量。加載因子 是哈希表在其容量自動增加之前可以達到多滿的一種尺度谋币。當哈希表中的條目數(shù)超出了加載因子與當前容量的乘積時仗扬,則要對該哈希表進行 rehash 操作(即重建內(nèi)部數(shù)據(jù)結(jié)構(gòu)),從而哈希表將具有大約兩倍的桶數(shù)蕾额。
//HashMap的數(shù)據(jù)存儲是Node類型的數(shù)組早芭,Hash的鍵值對是存儲在Node中的,從下面的結(jié)構(gòu)可以看出诅蝶,//Node其實就是一個單項列表退个,
//Node實現(xiàn)了Map.Entry 接口,即實現(xiàn)getKey(), getValue(), setValue(V value), equals(Object o), hashCode(這些函數(shù)调炬。
//這些都是基本的讀取/修改key帜乞、value值的函數(shù)。
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
public final K getKey() { return key; }
public final V getValue() { return value; }
public final String toString() { return key + "=" + value; }
public final int hashCode() {
return Objects.hashCode(key) ^ Objects.hashCode(value);
}
public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
public final boolean equals(Object o) {
if (o == this)
return true;
if (o instanceof Map.Entry) {
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
if (Objects.equals(key, e.getKey()) &&
Objects.equals(value, e.getValue()))
return true;
}
return false;
}
}
//插入數(shù)據(jù)
//往HashMap中傳入數(shù)據(jù)
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true); //獲取鍵的hash值 調(diào)用添加
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0) //如果當前map中沒有數(shù)據(jù) 對數(shù)組進行初始化操作
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null) //通過數(shù)組長度和hash找到的索引位置沒有數(shù)據(jù) 則直接添加到找到的索引位置
tab[i] = newNode(hash, key, value, null); //把新創(chuàng)建的Node賦值到索引位置上
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k)))) //判斷當前位置上 key是否相同
e = p;
else if (p instanceof TreeNode) //判斷當前位置數(shù)據(jù)是否為樹結(jié)構(gòu)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) { //循環(huán)把新數(shù)據(jù)添加到鏈表末尾
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st 如果桶里面的數(shù)據(jù)超過了TREEIFY_THRESHOLD值筐眷,就把這個桶從鏈表結(jié)構(gòu)變成紅黑樹結(jié)構(gòu)
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) //鏈表上key相同
break;
p = e;
}
}
if (e != null) { // existing mapping for key //是否key相同
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null) //onlyIfAbsent 如果為true則不修改key的位置的value值 否則替換成新的value值
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold) //size大于HashMap的閾值 對數(shù)組大小進行自增
resize();
afterNodeInsertion(evict); //用于LinkedHashMap 回調(diào)函數(shù)
return null;
}
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;
int oldCap = (oldTab == null) ? 0 : oldTab.length;
int oldThr = threshold;
int newCap, newThr = 0;
if (oldCap > 0) {
if (oldCap >= MAXIMUM_CAPACITY) { //當前容量大于最大數(shù)組容量 就把閾值設(shè)置為Integer的最大值 數(shù)組不在進行自增
threshold = Integer.MAX_VALUE;
return oldTab;
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && //給閾值和數(shù)組容量大大小增大兩倍黎烈,增大容量不大于最大數(shù)組容量
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold
}
else if (oldThr > 0) // initial capacity was placed in threshold //默認閾值大于0 ,在帶參創(chuàng)建的時候會產(chǎn)生這種情況
newCap = oldThr;
else { // zero initial threshold signifies using defaults
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); //否則按HashMap自帶的默認參數(shù)構(gòu)建新的數(shù)組和閾值的大小
}
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
threshold = newThr; //新的閾值設(shè)置為我們上面新建的閾值
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab; //創(chuàng)建新的數(shù)組
if (oldTab != null) { //如果當前node中數(shù)組 則把數(shù)據(jù)填充到新的數(shù)組里面
// 根據(jù)容量進行循環(huán)整個數(shù)組匀谣,將非空元素進行復制
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e;
// 獲取數(shù)組的第j個元素
if ((e = oldTab[j]) != null) {
oldTab[j] = null;
if (e.next == null) //如果鏈表只有一個數(shù)據(jù) 則直接按Hash的算法進行賦值
newTab[e.hash & (newCap - 1)] = e;
else if (e instanceof TreeNode) //如果為空黑樹 則直接通過紅黑樹方式進行添加
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
else { // preserve order
// 進行鏈表復制
// 方法比較特殊: 它并沒有重新計算元素在數(shù)組中的位置
// 而是采用了 原始位置加原數(shù)組長度的方法計算得到位置
Node<K,V> loHead = null, loTail = null;
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
do {
next = e.next;
if ((e.hash & oldCap) == 0) { //得到的是 元素的在數(shù)組中的位置是否需要移動
if (loTail == null)
loHead = e; //確定首元素
else
loTail.next = e; //循環(huán)添加
loTail = e;
}
else { //跟上面同理
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead; //如果數(shù)組位置沒變 直接鏈表添加上去
}
if (hiTail != null) { //如果數(shù)組位置改變 新的鏈表位置是 老位置加老的容量大小的位置
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}
public void putAll(Map<? extends K, ? extends V> m) { //添加一組Map數(shù)據(jù)到HashMap中來
putMapEntries(m, true);
}
final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
int s = m.size();
if (s > 0) {
if (table == null) { // pre-size //如果當前數(shù)據(jù)為空 按傳入集合的大小生成閾值
float ft = ((float)s / loadFactor) + 1.0F;
int t = ((ft < (float)MAXIMUM_CAPACITY) ?
(int)ft : MAXIMUM_CAPACITY);
if (t > threshold)
threshold = tableSizeFor(t);
}
else if (s > threshold) //如果容量大于閾值 對數(shù)組進行自增
resize();
for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) { //循環(huán)調(diào)用putVal添加數(shù)據(jù)
K key = e.getKey();
V value = e.getValue();
putVal(hash(key), key, value, false, evict);
}
}
}
//獲取數(shù)據(jù)
public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
final Node<K,V> getNode(int hash, Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) { //符合條件判斷 first = tab[(n - 1) & hash]) != null判斷這個位置是否存在數(shù)據(jù)
if (first.hash == hash && // always check first node //如果當前first就是查詢的數(shù)據(jù)直接返回
((k = first.key) == key || (key != null && key.equals(k))))
return first;
if ((e = first.next) != null) { //如果first鏈表下面有數(shù)據(jù)
if (first instanceof TreeNode) //紅黑樹查詢
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
do { //遍歷查詢鏈表
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
return null;
}
//刪除數(shù)據(jù)
public V remove(Object key) {
Node<K,V> e;
return (e = removeNode(hash(key), key, null, false, true)) == null ?
null : e.value;
}
final Node<K,V> removeNode(int hash, Object key, Object value,
boolean matchValue, boolean movable) {
Node<K,V>[] tab; Node<K,V> p; int n, index;
if ((tab = table) != null && (n = tab.length) > 0 &&
(p = tab[index = (n - 1) & hash]) != null) { //通過key查詢到數(shù)組下標位置 取出鏈表
Node<K,V> node = null, e; K k; V v;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
node = p;
else if ((e = p.next) != null) {
if (p instanceof TreeNode)
node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
else {
do {
if (e.hash == hash &&
((k = e.key) == key ||
(key != null && key.equals(k)))) {
node = e;
break;
}
p = e;
} while ((e = e.next) != null);
}
}
//上面為查出數(shù)據(jù)
if (node != null && (!matchValue || (v = node.value) == value ||
(value != null && value.equals(v)))) { //數(shù)據(jù)判斷是否需要匹配value
if (node instanceof TreeNode) //如果是紅黑樹 通過紅黑樹刪除
((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
else if (node == p) //如果為鏈表表頭 直接賦值為下一個
tab[index] = node.next;
else
p.next = node.next;
++modCount;
--size;
afterNodeRemoval(node);
return node;
}
}
return null;
}
HashMap的遍歷方式
//entrySet方式遍歷
Iterator<Map.Entry<String,String>> iter = map.entrySet().iterator();
while (iter.hasNext()){
Map.Entry<String,String> entry = iter.next();
String key = entry.getKey();
String value = entry.getValue();
}
//KeySet方式遍歷
Iterator<String> iter = map.keySet().iterator();
while (iter.hasNext()){
String key = iter.next();
String value = map.get(key);
}
//value遍歷
Iterator<String> iter = map.values().iterator();
while (iter.hasNext()){
String value = iter.next();
}
推薦使用第一種方式