1. 程式人生 > >HashMap原始碼分析(jdk1.8)

HashMap原始碼分析(jdk1.8)

private static final long serialVersionUID = 362498820763181265L;

     //The default initial capacity - MUST be a power of two.

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; //jdk1.6直接寫16,這效率更快??

     // The maximum capacity,MUST be a power of two <= 1<<30.

static final 

int MAXIMUM_CAPACITY = 1 << 30; // 2的30次方

static final float DEFAULT_LOAD_FACTOR = 0.75f; //填充比,裝載因子

/**(jdk1.8新加

* The bin count threshold for using a tree rather than list for a

     * bin.當add一個元素到某個位桶,其連結串列長度達到8時將連結串列轉換為紅黑樹.

     * 2< value<=8 時to mesh with assumptions in  

tree removal about conversion back to plain bins upon shrinkage.

     *連結串列轉為binCount>=TREEIFY_THRESHOLD-1,-1 for 1st。

     */ //當某個桶中的鍵值對數量大於8個【9個起】,且桶數量大於等於64,則將底層實現從連結串列轉為紅黑樹

       // 如果桶中的鍵值對達到該閥值,則檢測桶數量

static final int TREEIFY_THRESHOLD= 8; //jdk1.8新加

    /**      * The bin count threshold for untreeifying a (split) bin during a
     * resize operation. Should be less than TREEIFY_THRESHOLD, and at      * most 6 to mesh with shrinkage detection under removal.     * 僅用於TreeNode的final void split(HashMap<K,V> map, Node<K,V>[] tabint indexint bit) {             if (lc <= UNTREEIFY_THRESHOLD)  
//太小則轉為連結串列
                   tab[index] = loHead.untreeify(map); 
        }      */

static final int UNTREEIFY_THRESHOLD = 6;//jdk1.8新加

    /**      * The smallest table capacity for which bins may be treeified.      * (Otherwise the table is resized if too many nodes in a bin.)      * Should be at least 4 * TREEIFY_THRESHOLD to avoid conflicts      * between resizing and treeification thresholds.      *連結串列轉樹時,if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)             resize(); // 即不轉為樹了      */  //當桶數量到達64個的時候,才可能將連結串列轉為紅黑樹

static final int MIN_TREEIFY_CAPACITY = 64;//jdk1.8新加

/* ---------------- Fields -------------- */

    // jdk1.6 為 transient Entry[] table;

transient Node<K,V>[] table; //儲存元素(位桶)的陣列,length power of two

transient Set<Map.Entry<K,V>> entrySet;

transient int size; // key-value對,實際容量

transient int modCount; //結構改變次數,fast-fail機制

int threshold; // 新的擴容resize臨界值,當實際大小(容量*填充比)大於臨界值時,會進行2倍擴容

final float loadFactor;

Node 內部類

是HashMap內部類(jdk1.6就是Entry),繼承自 Map.Entry這個內部介面,它就是儲存一對對映關係的最小單元,也就是說key,value實際儲存在Node中。與1.6相比,修改了 hashCode()equals()方法【直接呼叫Object的hashCode、equals方法,而不是copy程式碼過來】,去掉了toString()、recordAccess(HashMap<K,V> m)【the value in an entry is overwritten時呼叫】、recordRemoval(HashMap<K,V> m)【remove entry時呼叫】方法。【鍵值對】

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; }
      // 由直接實現 變為 呼叫Object的HashCode,實際是一樣的
        public final int hashCode() {
            return Objects.hashCode(key) ^ Objects.hashCode(value);
        } //按位異或^不同為真,數a兩次異或同一個數b(a=a^b^b)仍然為原值a。
        public final V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            returnoldValue;
        }  // 優化邏輯
        public final boolean equals(Object o) {//改為呼叫Object的equals
            if (o == this) //記憶體地址(1.8新增)
                return true;
            if (o instanceof Map.Entry) {//1.6中!(instanceof)返回false
                Map.Entry<?,?> e = (Map.Entry<?,?>)o; //新加<?,?>泛型
                if (Objects.equals(key, e.getKey()) &&
                    Objects.equals(value, e.getValue()))
                    return true;
            }
            return false;
        }
    }
/*  jdk 1.6 Entry 的equals方法
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;
        } */


// 新增的紅黑樹,繼承LinkedHashMap.Entry<K,V>
static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
        TreeNode<K,V> parent;  // red-black tree links
        TreeNode<K,V> left;
        TreeNode<K,V> right;
        TreeNode<K,V> prev; // needed to unlink next upon deletion,節點的前一個節點
        boolean red; //true表示紅節點,false表示黑節點
        TreeNode(int hash, K key, V val, Node<K,V> next) {
            super(hash, key, val, next);
        }
        /**
         * Returns root of tree containing this node.獲取紅黑樹的根
         */
        final TreeNode<K,V> root() { 
           for (TreeNode<K,V> r=this, p;;){//p定義,int a=1,b;不能直接輸出b(未初始化)
                if ((p = r.parent) == null)  //若改為類似並查集的路徑壓縮(結構改變)
                    return r;
                r = p;
            }
        }
        /**
         * Ensures that the given root is the first node of its bin.
         */ //確保root是桶中的第一個元素,將root移到桶中的第一個【平衡思想】
        static <K,V> void moveRootToFront(Node<K,V>[] tab, TreeNode<K,V> root) {}
        /**
         * Finds the node starting at root p with the given hash and key.
         * The kc argument caches comparableClassFor(key) upon first use
         * comparing keys.
         *///查詢hash為h,key為k的節點  
        final TreeNode<K,V> find(int h, Object k, Class<?> kc) { // 詳見get相關
              TreeNode<K,V> p = this;   ……   }
        /**
         * Calls find for root node.
         */ //獲取樹節點,通過根節點查詢 
        final TreeNode<K,V> getTreeNode(int h, Object k) { // 詳見get相關
            return ((parent != null) ? root() : this).find(h, k, null);
        }
        /**
         * Tie-breaking utility for ordering insertions when equal
         * hashCodes and non-comparable. We don't require a total
         * order, just a consistent insertion rule to maintain
         * equivalence across rebalancings. Tie-breaking further than
         * necessary simplifies testing a bit.
         */ //比較2個物件的大小 
        static int tieBreakOrder(Object a, Object b) {}
        /**
         * Forms tree of the nodes linked from this node.
         * @return root of tree
         */ //將連結串列轉為二叉樹
        finalvoid treeify(Node<K,V>[] tab) {} //根節點設定為黑色 
        /**
         * Returns a list of non-TreeNodes replacing those linked from
         * this node.
         */ //將二叉樹轉為連結串列 
        final Node<K,V> untreeify(HashMap<K,V> map) {}
        /**
         * Tree version of putVal.
         */ //新增一個鍵值對 
        final TreeNode<K,V> putTreeVal(HashMap<K,V> map, Node<K,V>[] tab,
                                       inth, K k, V v) {}
        /**
         * Removes the given node, that must be present before this call.
         * This is messier than typical red-black deletion code because we
         * cannot swap the contents of an interior node with a leaf
         * successor that is pinned by "next" pointers that are accessible
         * independently during traversal. So instead we swap the tree
         * linkages. If the current tree appears to have too few nodes,
         * the bin is converted back to a plain bin. (The test triggers
         * somewhere between 2 and 6 nodes, depending on tree structure).
         */
        final void removeTreeNode(HashMap<K,V> map, Node<K,V>[] tab,
                                  boolean movable) {}
        /**
         * Splits nodes in a tree bin into lower and upper tree bins,
         * or untreeifies if now too small. Called only from resize;
         * see above discussion about split bits and indices.
         *
         * @param map the map
         * @param tab the table for recording bin heads
         * @param index the index of the table being split
         * @param bit the bit of hash to split on
         */ //將結點太多的桶分割  
        finalvoid split(HashMap<K,V> map, Node<K,V>[] tab, intindex, intbit) {}
        /* --------------------------------------------------*/
        // Red-black tree methods, all adapted from CLR
        //左旋轉
        static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root,
                                              TreeNode<K,V> p) {}
        //右旋轉
        static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root,
                                               TreeNode<K,V> p) {}
         //保證插入後平衡,共5種插入情況
        static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,
                                                    TreeNode<K,V> x) {}
         //刪除後調整平衡 ,共6種刪除情況
        static <K,V> TreeNode<K,V> balanceDeletion(TreeNode<K,V> root,
                                                   TreeNode<K,V> x) {}
        /**
         * Recursive invariant check
         */ //檢測是否符合紅黑樹 
        static <K,V> boolean checkInvariants(TreeNode<K,V> t) {}
}

static final int hash(Object key) { // 計算key的hash值hash(key)

int h;

return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);

}

n = tab.length

table的下標【bucket的index】:(n - 1) & hash


由上可知:

相關推薦

HashMap原始碼分析JDK1.8- 你該知道的都在這裡了

       HashMap是Java和Android程式設計師的基本功, JDK1.8對HashMap進行了優化, 你真正理解它了嗎? 考慮如下問題:  1、雜湊基本原理?(答:散列表、hash碰撞、連結串列、紅黑樹)2、hashmap查詢的時間複雜度, 影響因素和原理?

java集合4HashMap原始碼分析jdk1.8

前言 Map介面雖然也是集合體系中的重要一個分支,但是Map介面並不繼承自Collection,而是自成一派。 public interface Map<K,V> Map集合儲存鍵對映到值的物件。一個集合中不能包含重複的鍵,每個鍵最多

HashMap原始碼分析jdk1.8

private static final long serialVersionUID = 362498820763181265L;      //The default initial capacity - MUST be a power of two. static final 

HashMap主要方法原始碼分析JDK1.8

本篇從HashMap的put、get、remove方法入手,分析原始碼流程 (不涉及紅黑樹的具體演算法) jkd1.8中HashMap的結構為陣列、連結串列、紅黑樹的形式     (未轉化紅黑樹時)    (轉化為紅黑樹時的情況)   一、關於

java中排序原始碼分析JDK1.8

List排序 在開發過程中常用的是jdk自帶的排序 Collections.sort(List<T> list, Comparator<? super T> c); 開啟原始碼如下: @SuppressWarnings({"unchecked",

【Java集合類】LinkedList原始碼分析jdk1.8

ArrayList和LinkedList是List介面的兩種實現,具有相同的查詢、插入、刪除操作,只是底層的實現方式不一樣。LinkedList是以雙向連結串列形式實現的集合類。 其增刪操作由於不需要移

HashSet 原始碼分析jdk1.8

類繼承關係: (*=>:介面實現) java.lang.Object    –java.util.AbstractCollection =>Collection      –java.util.AbstractSet =>Set        –java

ArrayList原始碼分析JDK1.8

不積跬步,無以至千里;不積小流,無以成江海。從基礎做起,一點點積累,加油! ArrayList的定義 public class ArrayList<E> extends AbstractList<E>implements

TreeMap原始碼分析jdk1.8

TreeMap的基本概念: TreeMap集合是基於紅黑樹(Red-Black tree)的 NavigableMap實現。該集合最重要的特點就是可排序,該對映根據其鍵的自然順序進行排序,或者根據建立對映時提供的 Comparator 進行排序,具體取決於使

java集合6:TreeMap原始碼分析jdk1.8

前言 TreeMap的基本概念: TreeMap集合是基於紅黑樹(Red-Black tree)的 NavigableMap實現。該集合最重要的特點就是可排序,該對映根據其鍵的自然順序進行排序,或者根據建立對映時提供的 Comparator 進行排序,具體

java集合7:TreeSet原始碼分析jdk1.8

前言 TreeSet是基於 TreeMap 的 NavigableSet 實現。使用元素的自然順序對元素進行排序,或者根據建立 set 時提供的 Comparator 進行排序,具體取決於使用的構造方法。這句話什麼意思呢?就是說,跟HashSet底層是一個Ha

LinkedList原始碼分析jdk1.8

LinkedList概述 ​ LinkedList 是 Java 集合框架中一個重要的實現,我們先簡述一下LinkedList的一些特點: LinkedList底層採用的雙向連結串列結構; LinkedList支援空值和重複值(List的特點); LinkedList實現Deque介面,具有雙端佇列的特性,

jdk1.8 HashMap原始碼分析resize函式

final Node<K,V>[] resize() { Node<K,V>[] oldTab = table; int oldCap = (oldTab == null) ? 0 : oldTab.len

HashMap源碼分析JDK1.8

mage -s ret 增刪 函數 tno png log 唯一性 一、HashMap簡介   HashMap是一種基於數組+鏈表+紅黑樹的數據結構,其中紅黑樹部分在JDK1.8後引入,當鏈表長度大於8的時候轉換為紅黑樹。   HashMap繼承於AbstractMap(M

HashMap分析JDK1.8

這裡是基於JDK1.8。 可以看出HashMap繼承了AbstractMap,實現了Map。 先看看HashMap中的幾個關鍵的屬性: 預設初始容量是16: 也很好理解,1的二進位制還是1: 向左位移四位: 最大容量很大: 負載因子,主要

LinkedHashMap 原始碼詳細分析JDK1.8

1. 概述 LinkedHashMap 繼承自 HashMap,在 HashMap 基礎上,通過維護一條雙向連結串列,解決了 HashMap 不能隨時保持遍歷順序和插入順序一致的問題。除此之外,LinkedHashMap 對訪問順序也提供了相關支援。在一些場景下,該特性很有用,比如快取。在實現上

Object原始碼解析JDK1.8

package java.lang; public class Object { /** * 一個本地方法,具體是用C(C++)在DLL中實現的,然後通過JNI呼叫 */ private static native void registerN

String原始碼解析JDK1.8

1、string類的定義 public final class String implements java.io.Serializable, Comparable<String>, CharSequence {} java.io.Serializa

ConcurrentHashMap原始碼解析JDK1.8

package java.util.concurrent; import java.io.ObjectStreamField; import java.io.Serializable; import java.lang.reflect.ParameterizedType;

AbstractQueuedSynchronizer1原始碼解析jdk1.8

AbstractQueuedSynchronizer原始碼解析(簡稱AQS) 1.執行流程簡介 第一個執行緒呼叫 reentrantLock.lock(),翻到最前面可以發現,tryAcquire(1) 直接就返回 true了,結束。只是設定了 state=1(連 hea