Java 基礎 —— HashMap

Java 基礎 —— HashMap

簡介

HashMap 用於存放鍵值對,基於哈希表的Map接口實現,屬於常用Java集合之一。

  • JDK1.8之前HashMap由 數組 + 鏈表 組成,數組是HashMap的主體,鏈表則是主要爲了解決哈希衝突而存在的的(“拉鍊法”解決衝突)
  • JDK1.8之後在解決哈希衝突時有較大改變,當鏈表長度大於閥值(默認爲8)時,將鏈表轉換爲紅黑樹,以減少搜索時間。

底層數據結構分析

JDK1.8前

1.8前底層是數組和鏈表結合在一起,也就是鏈表散列

  • HashMap 通過 Key 的 hashCode經過擾動函數處理過後得到hash值(一個近似值),然後通過(n - 1) & hash判斷當前元素存放的位置(這裏的n指的是數組的長度),如果當前位置存在元素的話,就判斷該元素與要存入的元素的hash值以及key是否相同,如果相同的話,直接覆蓋,不相同就通過鏈表法解決衝突。
    擾動函數: 就是HashMap的hash方法。使用 hash 方法即是擾動函數,是爲了防止一些實現比較差的 hashCode() 方法 換句話說使用擾動函數之後可以減少碰撞。

JDK 1.8 HashMap的hash方法源碼

/**
  * JDK 1.8 的 hash方法 相比於 JDK 1.7 hash 方法更加簡化,但是原理不變。
  * @param key
  * @return
  */
static final int hash(Object key) {
    int h;
    // key.hashCode():返回散列值也就是hashcode
    // ^ :按位異或
    // >>>:無符號右移,忽略符號位,空位都以0補齊
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

對比 JDK 1.7 HashMap的hash方法源碼

static int hash(int h) {
    // This function ensures that hashCodes that differ only by
    // constant multiples at each bit position have a bounded
    // number of collisions (approximately 8 at default load factor).

    h ^= (h >>> 20) ^ (h >>> 12);
    return h ^ (h >>> 7) ^ (h >>> 4);
}

結論

  • 1.7的hash方法性能稍差一點點,因爲擾動了4次
  • 拉鍊法
    將鏈表和數組相結合。也就是說創建一個鏈表數組,數組中每一格就是一個鏈表。若遇到哈希衝突,則將衝突的值加到鏈表中即可。
    在這裏插入圖片描述

JDK1.8後

相比於之前的版本,jdk1.8在解決哈希衝突時有了較大的變化,當鏈表長度大於閥值(默認爲8)時,將鏈表轉化爲紅黑樹 ,以減少搜索時間。
在這裏插入圖片描述

類屬性

public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {
    // 序列號
    private static final long serialVersionUID = 362498820763181265L;    
    // 默認的初始容量是16
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;   
    // 最大容量
    static final int MAXIMUM_CAPACITY = 1 << 30; 
    // 默認的填充因子
    static final float DEFAULT_LOAD_FACTOR = 0.75f;
    // 當桶(bucket)上的結點數大於這個值時會轉成紅黑樹
    static final int TREEIFY_THRESHOLD = 8; 
    // 當桶(bucket)上的結點數小於這個值時樹轉鏈表
    static final int UNTREEIFY_THRESHOLD = 6;
    // 桶中結構轉化爲紅黑樹對應的table的最小大小
    static final int MIN_TREEIFY_CAPACITY = 64;
    // 存儲元素的數組,總是2的冪次倍
    transient Node<k,v>[] table; 
    // 存放具體元素的集
    transient Set<map.entry<k,v>> entrySet;
    // 存放元素的個數,注意這個不等於數組的長度。
    transient int size;
    // 每次擴容和更改map結構的計數器
    transient int modCount;   
    // 臨界值 當實際大小(容量*填充因子)超過臨界值時,會進行擴容
    int threshold;
    // 加載因子
    final float loadFactor;
}

loadFactor 加載因子

threshold

Node 節點類源碼

// 繼承自 Map.Entry<K,V>
static class Node<K,V> implements Map.Entry<K,V> {

	   // 哈希值,存放元素到hashmap中時用來與其他元素hash值比較
       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; }
        
        // 重寫hashCode()方法
        public final int hashCode() {
            return Objects.hashCode(key) ^ Objects.hashCode(value);
        }

        public final V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }
        
        // 重寫 equals() 方法
        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;
        }
}

樹節點類源碼

static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
    // 父
    TreeNode<K, V> parent;
    // 左
    TreeNode<K, V> left;
    // 右
    TreeNode<K, V> right;
    // needed to unlink next upon deletion
    TreeNode<K, V> prev;
    // 判斷顏色
    boolean red;           

    TreeNode(int hash, K key, V val, Node<K, V> next) {
        super(hash, key, val, next);
    }

    // 返回根節點
    final TreeNode<K, V> root() {
        for (TreeNode<K, V> r = this, p; ; ) {
            if ((p = r.parent) == null)
                return r;
            r = p;
        }
    }
}

基礎關係圖

版本:JDK1.8
在這裏插入圖片描述

HashMap 源碼分析

繼承關係

實現關係

內部類

Holder
Entry
HashIterator
ValueIterator
KeyIterator
EntryIterator
KeySet
Values
EntrySet

函數意義

HashMap(int, float)
HashMap(int)
HashMap()
HashMap(Map<? extends K, ? extends V>)
roundUpToPowerOf2
inflateTable
init
initHashSeedAsNeeded
hash
indexFor
size
isEmpty
get
getForNullKey
containsKey
getEntry
put
putForNullKey
putForCreate
putAllForCreate
resize
transfer
putAll
remove
removeEntryForKey
removeMapping
clear
containsValue
containsNullValue
clone
addEntry
createEntry
newKeyIterator
newValueIterator
newEntryIterator
keySet
values
entrySet
entrySet0
writeObject
readObject
capacity
loadFactor

靜態常量

DEFAULT_INITIAL_CAPACITY
MAXIMUM_CAPACITY
DEFAULT_LOAD_FACTOR
EMPTY_TABLE
table
size
threshold
loadFactor
modCount
ALTERNATIVE_HASHING_THRESHOLD_DEFAULT
hashSeed
entrySet
serialVersionUID

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章