思考リサイズを最適化するHashMapの方法:JDKソースを書き直し

        ヒント:この推論は、正確ではありませんほとんどの場合、ハッシュ値は、HashMapの内の同じ位置にある配列の異なる要素であるため、しかし、全体の思考プロセスが完了すると、興味が見ることができます。


あまり話をしなかった、私たちは直接リサイズメソッドHashMapのソースを参照してください。

フォーカス715から744行、私はその結論を指示、私は次のように、ほぼ30行を置き換えるためのコード行を使用します。

newTab[e.hash & (newCap - 1)] = e;

あなたは、おそらくそれを見て、これ以上 あなたが直接これら二つを統合決めることができるように判断した後e.next == 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) {
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // double threshold
        }
        else if (oldThr > 0) // initial capacity was placed in threshold
            newCap = oldThr;
        else {               // zero initial threshold signifies using defaults
            newCap = DEFAULT_INITIAL_CAPACITY;
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }
        if (newThr == 0) {
            float ft = (float)newCap * loadFactor;
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);
        }
        threshold = newThr;
        @SuppressWarnings({"rawtypes","unchecked"})
            Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
        table = newTab;
        if (oldTab != null) {
            for (int j = 0; j < oldCap; ++j) {
                Node<K,V> e;
                if ((e = oldTab[j]) != null) {
                    oldTab[j] = null;
                    if (e instanceof TreeNode)
                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                    else { // preserve order
                       newTab[e.hash & (newCap - 1)] = e;
                    }
                }
            }
        }
        return newTab;
    }

  

分析的思考:

:さんは私の思考プロセスは、実際には、私の最初の考えは、以下の、このバージョンが何であるかを分析してみましょう

if ((e.hash & oldCap) == 0) {
	newTab[j] = e;
}else {
	newTab[j+ oldCap]= e;
}

あなたがより良い感じることができる、コード2の次の行があります:

  newTab[e.hash & oldCap == 0?j:j+ oldCap] = e;

終了することができ、私はここにいた可能性がありますが、私は突然、あなたが直接マージすることができますので、それは、そのためで始まるコード行、および712行のソースは、同様に、より簡潔なことができました。

私は、この最適化を発見した後、テストにテストのホールドを取得するにはHashMapの継承クラスを考慮することであるが、この方法は、公開サイズ変更が変更され、最終的なされていないキーワードを変更され、それが全体のHashMapのソースコードをコピーして、変更している必要がありますそのサイズ変更方法の検証は、私はあまりにも多くの問題を考えて、実現することができますが、必要はありませんでした。

だからここに、私は途中で私の最適化リバースプッシュの精度を検証するために原作者の考え方になります。我々は目的を区別することであり、これは、最初に定義loHead、loTailとhiHead、hiTailにこれらの4つの変数、コードのサイズ変更方法の715から744行を分析し続ける(e.hash&oldCap)== 0かつ(e.hash&ハッシュマップのe.hash&2N-1の拡大のみが元の配列添字のインデックス値とすることができるのでoldCap)!= 0は、このような場合はj個のインデックス、または配列の添字プラスオリジナルオリジナルの配列の長さ  J + oldCapしかし、実際には、4つの変数を定義する必要、唯一の2つのヘッドとテール缶を定義する必要がありません。なぜ?今、私たちは、whileループに焦点を当ててコードを見て:

キーライン721、見ることができるe.hash()とoldCapこれら2つの変数が 行う-whileループ定数であり、Eは、元の配列の同じ位置のリスト全体であるので、それらは値をハッシュそれは等しくなければなりません。この結論に基づいて、我々は次のように彼は、原作者にコードの715から744行を思っ最適化することができます:

                        Node<K,V> head = null, tail = null;
                        Node<K,V> next;
                        do {
                            next = e.next;
                            if (tail == null)
                                head = e;
                            else
                                tail.next = e;
                            tail = e;
                        } while ((e = next) != null);
                        if (tail != null) {
                            tail.next = null;
                            newTab[(head.hash & oldCap) == 0?j:j+ oldCap] =  head;
                        }

本来の機能を完了するための唯一の頭と尾2つの変数。ここに書いた、私はあなたがおそらく知っていると思う、上記のコード分析は、実際には、頭と尾変数と少し実用的で、見ることができます。行う-whileループの終了後にそれはe線588で決定されているので、第1テールが空であるかどうかを決定し、この行は、完全に必要ではない以下の、nullでありません。

だから、空ではありません確かにループテールを入力した後、その後、新しい配列に向かいます、配列のインデックス位置を取得するために操作した後、ビットのハッシュ値oldCapヘッドを使用し、この行は、単純に不必要であるため、ヘッド=割り当ての最初のサイクルは、それが全体のループがdo-ながら、直接必要ではないだけE これは私が最適化を始めていることを、コードのe線に置き換えられた後に電子とヘッドを交換するには、問題ではありません。

newTab[(e.hash & oldCap) == 0?j:j+ oldCap] = e;

それは次にe.hash固定値を発見しているので、そこに三元表現の必要がなく、直接(newCap - 1)&e.hashライン上添字指標を与えます。

 newTab[e.hash & (newCap - 1)] = e;

だから我々は、我々は、この最適化されたコードの原作者が立ち上げたと思いました。

要約:

私が最初にこのコードの715から744行は再びそれを読んだ後、常に少しぎこちないを感じ、やや不可解で見て、私は結論に到達することNEWTAB [J] = E;

したがって、この行の後に思考と分析した後、コードを最適化されています:

 newTab[e.hash & (newCap - 1)] = e;

また、ソースコード中にあなたは、あまりにも包括的なビット派手に感じることができます。実際には、我々はあなたがオリジナルの715から744ラインの全体の目的を見つけるだろう、振り返ると考えるが新しい配列への配列の順序は、データ移行時間の拡大でjdk1.7無限ループを避けるために、リストにコピーされ、それは実際には非常に簡単です行うことができます

生徒は問題がある場合は、コメントの中指で出てくることができます

公開された25元の記事 ウォン称賛51 ビュー20000 +

おすすめ

転載: blog.csdn.net/Royal_lr/article/details/103196010