JDKのソースコードのあれらの事の私の目の中のHashMap


ソース部分はHashMapから言えば、筆者がこの種類のソース部分を何度も見たからだ.同時に、ネット上では大雑把な紹介が多いような気がする.間違いがあったらコメントで指摘してください.修正をタイムリーに検証します.ありがとうございます.
次に私の目に映るHashMapについてお話しします.
jdkバージョン:1.8
ソースコードに深く入り込む前に、HashMapの全体的な構造を理解することは非常に重要なことであり、構造もソースコードのいくつかのHashMapに対する操作を体現しており、構造は大体以下の通りである.
上の構造図からもHashMapの実現構造: + + が見られるはずです.
類の注釈を見て、直接ソースの部分を見るのが最も良くて、大多数はすべて理解できないかもしれなくて、ここは他の人の翻訳を見ることができます:類の注釈の翻訳.本文の中で筆者は赤黒樹の部分について説明するつもりはなく、挿入と削除操作は様々な状態を引き起こし、対応する調整が必要であり、その後は単独で赤黒樹の基礎を書き、TreeNodeと結びつけて説明する.
まず、いくつかの名詞の概念をまとめて、初心者に理解しやすいようにします.
1.バケツ(bucket):配列に要素が格納されている場所、構造図を参照すると、実際には配列のインデックスの下の要素であり、この要素は木のルートノードまたはチェーンテーブルの最初のノードである可能性があります.もちろん、チェーンテーブルまたは赤と黒の木全体がバケツとして認識されています.
2.bin:バケツの各要素、すなわち赤黒ツリーの要素またはチェーンテーブルの要素.
上の名詞のほかに、ハッシュ表を理解したほうがいいので、参考にしてください.HashMapもハッシュ表の一つの実現であり、簡単に理解することができ、数学における余剰操作を類比することができ、範囲を固定し、大量のデータを境界のある範囲に入れ、余剰配置を求める.この操作はハッシュ表の一つの実現方式である.
次に、ソース・セクションの説明を行います.
クラス定義
public class HashMap extends AbstractMap
    implements Map, Cloneable, Serializable

AbstraactMapを継承してCloneableインタフェースを実現し、クローン機能を提供してSerializableインタフェースを実現し、シーケンス化をサポートし、シーケンス化伝送を便利にする
ここで興味深い質問があります.なぜHashMapはAbstractMapを継承し、Mapインタフェースを実現しなければならないのですか.興味のある方はstackoverflowの回答を見てみましょう.
https://stackoverflow.com/que...
変数の説明
    /**
     * Node       ,16
     */
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

    /**
     * Node       ,      
     */
 
    /**
     *       
     *        ?
     *                          。
     *                        ,         rehash  (    )。
     */
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

    /**
     *          ,                 ,
     *   ,       ,           。
     */
    static final int TREEIFY_THRESHOLD = 8;

    /**
     *    resize   ,                      
     */
    static final int UNTREEIFY_THRESHOLD = 6;

    /**
     *              ,
     *               resize    
     *            
     */
    static final int MIN_TREEIFY_CAPACITY = 64;
    /**
     * 
     *              ,put         
     *             ,       
     */
    transient Node[] table;

    /**
     * 
     * entrySet  key value     
     */
    transient Set> entrySet;

    /**
     * 
     *        ,         
     */
    transient int size;

    /**
     * 
     *    ,fail-fast    ,     ,      google 
     *                    ,    :
     *     A    a,modCount=expectedModCount  1,     ,    B   a,modCount=2,  A       modCount<>expectedModCount,  
     *   ,         ,       
     */
    transient int modCount;

    /**
     * 
     *   ,         (  *    )   ,     
     */
    int threshold;

    /**
     * 
     *     
     */
    final float loadFactor;

方法を見る前にNode実装を見てください.
    /**
     * Node    
     *                  next  
     *       ,       
     */
    static class Node implements Map.Entry {
        final int hash;
        final K key;
        V value;
        Node next;

        Node(int hash, K key, V value, Node 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;
        }
        
        /**
         * Map.Entry     
         *               
         */
        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;
        }
    }

重点説明
注釈には、プロセスの整理を支援するタグを追加します.また、後で対照と参照をまとめるのに便利です(例えば、A 1、A 2は同じレベルです).
    /**
     *            0.75f
     * A1
     */
    public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }
    /**
     *         ,       
     * A2
     */
    public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }
    /**
     *            
     * A2
     */   
    public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);
        this.loadFactor = loadFactor;
        //           
        //      ,   ,         
        //    resize  ,resize       ,                               
        //       :https://www.cnblogs.com/liujinhong/p/6576543.html
        //                 ,            
        this.threshold = tableSizeFor(initialCapacity);
    }
    /**
     *  m    map 
     */  
    public HashMap(Map extends K, ? extends V> m) {
        this.loadFactor = DEFAULT_LOAD_FACTOR;
        putMapEntries(m, false);
    }
    /**
     * evict        ,      ,     afterNodeInsertion(evict),     
     *  LinkedHashMap    ,        
     */  
    final void putMapEntries(Map extends K, ? extends V> m, boolean evict) {
        int s = m.size();
        if (s > 0) {
            /**
             *   table        
             */
            if (table == null) { // pre-size
                //      ,  m          map             
                float ft = ((float)s / loadFactor) + 1.0F;
                int t = ((ft < (float)MAXIMUM_CAPACITY) ?
                         (int)ft : MAXIMUM_CAPACITY);
                //      t         
                if (t > threshold)
                    threshold = tableSizeFor(t);
            }
            else if (s > threshold)
                /**
                 *   map     ,             ,        
                 */  
                resize();
            /**
             *                ,    entrySet  putVal  m  Node   key value
             *          ,
             * putVal      ,hash(key),map put           
             *       :https://www.cnblogs.com/liujinhong/p/6576543.html
             *           ,              
             */  
            for (Map.Entry extends K, ? extends V> e : m.entrySet()) {
                K key = e.getKey();
                V value = e.getValue();
                putVal(hash(key), key, value, false, evict);
            }
        }
    }
    
    /**
     *     ,    
     * 
     *            ,                2 N  (          ),     HashMap(int initialCapacity),
     *         ,   resize          *     ,
     *     1000      threshold=1024,      ,         ,
     *            1024 * 0.75 = 768,   Node     1024,size=1,
     *    769    ,  resize  ,threshold=1536,Node     2048,         ,
     *           ,   HashMap      ,                   
     *          debug  
     */
    final Node[] resize() {
        //oldTab       Node  
        Node[] oldTab = table;
        // oldCap null    0,        Node       
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        //       
        int oldThr = threshold;
        //         (  ),      
        int newCap, newThr = 0;
        // 1.         
        // B1
        if (oldCap > 0) {
            //     Node               ,      ,     Integer.MAX_VALUE
            if (oldCap >= MAXIMUM_CAPACITY) {
                // C1
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
            //              2           ,
            //       Node               ,
            //               map    2 ,                   ,
            //   ,                map    2 
            //   newCap         
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                // C2
                newThr = oldThr << 1; // double threshold
        }
        // 2.                       
        //    oldCap = 0   ,  oldThr > 0               map
        //             ,threshold   0
        // B2
        else if (oldThr > 0) // initial capacity was placed in threshold
            //                  put        (   put  )
            //                
            //             ,                 1024
            newCap = oldThr;
        // 3.                       
        // B3
        else {               // zero initial threshold signifies using defaults
            //        =     ,       =      *     
            //        16,   12
            newCap = DEFAULT_INITIAL_CAPACITY;
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }
        /**
         *              ,     ,       newThr      ,             ,  newThr    
         *      0,     :
         *     :
         * 1.    node                    node            16
         * 2.        ,   put            newThr
         * D1
         */
        if (newThr == 0) {
            //         ft =     *     
            float ft = (float)newCap * loadFactor;
            //              ft              ft,     int   
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);
        }
        //     ,              
        //         
        threshold = newThr;
        @SuppressWarnings({"rawtypes","unchecked"})
        //       Node   
        Node[] newTab = (Node[])new Node[newCap];
        //        Node  ,                
        table = newTab;
        // E1
        if (oldTab != null) {
            //        ,              
            for (int j = 0; j < oldCap; ++j) {
                Node e;
                //          
                if ((e = oldTab[j]) != null) {
                    //         ,  GC
                    oldTab[j] = null;
                    if (e.next == null)
                        //     Node        ,     1
                        //                       ,
                        //        ?
                        //     ,        ,  hash (  ,   HashMap hash(key)       )
                        //         ,            ,        ,
                        // &      e.hash  n ,n newCap - 1           
                        //              16,    10000,   1111, 4 
                        //      tableSizeFor        16    
                        //   &                  ,  ,                      
                        //          putVal  ,       
                        newTab[e.hash & (newCap - 1)] = e;
                    else if (e instanceof TreeNode)
                        //     e.next   ,       ,
                        //        ,     ,      
                        //           ,                      ,
                        //        ,    ,           ,          。
                        ((TreeNode)e).split(this, newTab, j, oldCap);
                    else { // preserve order
                        /**
                         *      HashMap   ,
                         *         ,            
                         *         
                         *            ,    ,    16,      32(     ,         )
                         *          ?
                         *         ?
                         *                      
                         *        put        ,     ,putVal      
                         * (n - 1) & hash    newTab[e.hash & (newCap - 1)]       
                         *           ?         ?
                         *         ,      ,      
                         *          (hi       )   hiHead,hiTail
                         *         (lo       )   loHead,loTail
                         *        ?
                         *      ,                         
                         */
                        Node loHead = null, loTail = null;
                        Node hiHead = null, hiTail = null;
                        Node next;
                        //               
                        do {
                            next = e.next;
                            /**
                             * e.hash & oldCap = 0
                             *     ,              
                             *     :
                             * oldCap=32=100000(   ),newCap=64=1000000(   )
                             *                (oldCap-1) & hash
                             *  1    ,      0~oldCap-1
                             * e.hash & oldCap           1      
                             *     0,    0
                             *           ?
                             *      ,    ,  bucket( )  (             ) 
                             * (newCap-1) & hash (oldCap-1) & hash    ,      
                             *   32 64,    1  ,     
                             *           0,        bucket    ,       
                             *    0,  1,                 
                             *          ,   bucket    (     )    
                             *            ,  1  0
                             *       bucket ,       bucket(     bucket),
                             *        bucket(  bucket),
                             *                     ,      ,      
                             *       bucket             
                             *   32 64,   63-31=32=oldCap=10000(   )
                             *          oldCap
                             */
                            if ((e.hash & oldCap) == 0) {
                                //     Node     =        
                                //   loHead,loTail    ,    
                                if (loTail == null)
                                    //        ,     
                                    loHead = e;
                                else
                                    //       ,          ,next     
                                    loTail.next = e;
                                //        ,         
                                loTail = e;
                            }
                            else {
                                //     
                                if (hiTail == null)
                                    hiHead = e;
                                else
                                    hiTail.next = e;
                                hiTail = e;
                            }
                        } while ((e = next) != null);
                        
                        //         ,          ,                    
                        if (loTail != null) {
                            //        ,      ,   next    ,           next  
                            loTail.next = null;
                            //    j       j ,                    
                            newTab[j] = loHead;
                        }
                        if (hiTail != null) {
                            hiTail.next = null;
                            //               ,      oldCap,       
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
        return newTab;
    }

    /**
     * put      
     * hash,key hash ,    ,HashMap      
     * onlyIfAbsent,       ,true,      
     * evict,LinkedHashMap  afterNodeInsertion     ,           
     */    
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node[] tab; Node p; int n, i;
        // F1
        if ((tab = table) == null || (n = tab.length) == 0)
            // table      0 , table     ,        
            //                 ,         ,  putMapEntries  
            n = (tab = resize()).length;
        //                             (       )
        //         key
        // G1
        if ((p = tab[i = (n - 1) & hash]) == null)
            //    ,          
            tab[i] = newNode(hash, key, value, null);
        // G2
        else {
            //          ,      
            Node e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                //      (       )        hash   
                //   key   ,e p    ,        key
                // e  p     
                e = p;
            else if (p instanceof TreeNode)
                //          ,           ,           
                //              key,     Node,    null(     Node)
                e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);
            else {
                //         (             ),      ,
                //         ,        key     
                //   binCount      bin   ,     
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        // next                           next
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            //       ,          , treeifyBin         ,            
                            //    ,   bin      TREEIFY_THRESHOLD=8,        MIN_TREEIFY_CAPACITY=64       
                            //                 ,       
                            treeifyBin(tab, hash);
                        break;
                    }
                    //                   
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            //        map       key Node  ,         ,         key     
            // H1
            if (e != null) { // existing mapping for key
                //          
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    // onlyIfAbsent         ,    
                    e.value = value;
                //                    
                afterNodeAccess(e);
                //     
                return oldValue;
            }
        }
        ++modCount;
        //       ,    ,              1,     
        //         ,      
        // I1
        if (++size > threshold)
            resize();
        //  afterNodeAccess,                   
        afterNodeInsertion(evict);
        return null;
    }

ポイントの部分は上のいくつかの方法で、残りのソースコードの部分は一つ一つ貼って分析しないで、私の上で説明した部分を理解することができて、基本的に赤と黒の木とjdk 1.8の新しい特性の関連部分を除いて、残りの部分は基本的にすべて理解することができるべきで、ここで更に1つのシーケンス化の方面の問題を補充します:
なぜHashMapのtable変数をtransientに設定するのですか?この問題を理解する前に、シーケンス化コードwriteObjectとreadObjectを自分で見て、次のリンクを参考に考えてみましょう.
https://segmentfault.com/q/10...
HashMapでは、Entryの格納位置がKeyのHash値に基づいて計算され、配列に格納されるため、同じKeyに対して異なるJVM実装で計算されたHash値が異なる場合がある.私はもともとwindowマシンでAはNode配列の中で0の位置に置かれていたのですが、MacではNode配列の中で5の位置に置かれていたのかもしれませんが、修正しないと逆シーケンス化後もMacでは0の位置になり、これにより後続の増加ノードが錯乱し、私たちが望んでいた結果ではないので、シーケンス化でHashMapはキー値ペアごとのキーと値をシーケンス化し、全体ではなく、逆シーケンス化して1つずつ取り出し、位置の乱れを起こさない.
ではJDK 1.8でHashMapはマルチスレッド環境でデッドサイクルをもたらすのでしょうか?
上记の构造と処理过程の分析から见ると、できないはずですが、データの紛失が発生するかどうかは、私は検証しません.自分でGoogle、手書きコードで検証します.また、HashMapが非スレッドで安全であることを一般開発者が知っている場合は、マルチスレッドの場合はConcurrentHashMapを使えばいいと思います.後でConcurrentHashMapの分析も整理します.
まとめ
重点説明部分ではresizeとputの操作過程を詳しく説明しましたが、一部の新人はまだ整理できないかもしれません.私はここで日常の使用と結びつけて、全体の過程をまとめて、皆さんの理解を便利にします.
1.HashMap作成プロセス(通常の状態):
2.HashMap resizeプロセス(正常状態):
3.HashMapputプロセス(正常状態):
HashMapはまずその内部の実現構造を理解する必要がある: + + 、構造の基礎の上でソースコードに対して深く入り込んで、resizeとput操作は最も重要な2つの部分で、この2つのブロックを理解して、基本的にHashMapの全体の処理過程に対して一定の認知があって、また、必ず自分でdebugを手に入れて、データの変換を整理して、HashMapを知るのに役立ちます.
本文はまず基礎部分から話して、いくつかの名詞を説明して、ハッシュ表に言及して、構造を実現することから各位のもっと良い理解のソースコードの操作部分を助けて、重点のいくつかの部分に対して詳しい説明をして、resizeとputの操作の難点部分も相応の解釈をして、各位に対して役に立つことを望んで、後で暇があれば私は赤い黒い木の部分の理解を分かち合います.ありがとうございます.