JDK1.8のHashMap

jdk1.8後コースのJavaのHashMapの性質のデータ構造、彼は+の形でリンクされたリストに編成アレイである、鎖長が速いアドレッシングに8より大きいリストに修飾されています赤黒木。

それは、本質的に配列されているので、このアルゴリズムは、同じ配列を持つキーは、それぞれを算出することを確実にするため、我々は、最優先となっているアレイの位置に対応するキーに入れなければなりません結果(べき等)の一貫性を確保します。一部の学生は、非常にシンプルなああ言うことを、我々はキーを取得して、結果のhashCode配列の長さは、それを法。

このアプローチは確かに可能ですが、JDKは、それをしているのですか?なぜ?ソースコードを解析する必要があります。

まず第一に、限り必ず、HashMapの動作時間のためのハッシュマップのノウハウを利用し、学生が配列のキーの上に置かれるように、我々はのHashMapのソースコードでそれを置く見えます。

    /**
     * Associates the specified value with the specified key in this map.
     * If the map previously contained a mapping for the key, the old
     * value is replaced.
     *
     * @param key key with which the specified value is to be associated
     * @param value value to be associated with the specified key
     * @return the previous value associated with <tt>key</tt>, or
     *         <tt>null</tt> if there was no mapping for <tt>key</tt>.
     *         (A <tt>null</tt> return can also indicate that the map
     *         previously associated <tt>null</tt> with <tt>key</tt>.)
     */
    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }
// 可以看到put操作执行了putValue这个方法,参数有五个,但是第一个参数是获取的hash(Object key) 方法的返回结果,那我们一起来看一下这个方法

HashMapの中の話JDK1.8ハッシュアルゴリズム

ハッシュアルゴリズムのソースコードを次のように:

    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

この方法は、最初のint型のHのローカル変数を、非常に単純な宣言され、次にキーがnullは、それがnullである場合、直接0を返し、そうでなければ、計算のように計算されるか否かを判断します

キーのハッシュコード値を取得し、ローカル変数の時間、そして時間と16時間、右の結果に割り当てられたXOR演算

それは仮定のハッシュコード値の前に、単にケースです。

1101 1100 0011 1111 1111 1111 1111 1001

16ビット右シフトは、次のとおりです。

1111 1111 1111 0000 0000 0000 0000 1111

XOR(排他的論理和ここでは科学の簡単な計算式をクリックし、A、B二つの値が同一でない場合は、XOR結果が1の場合で、Bの値が同じで、排他的論理和の結果が0です。)後の結果は以下のとおりです。

1101 1100 0011 1111 1111 1111 1111 1001

1111 1111 1111 0000 0000 0000 0000 1111

0010 0011 1100 1111 1111 1111 1111 0110

私は、この方法は、私たちが理解できると信じていますが、理解するために読んで行くが、私は理由を理解していません。最初の秘密は、我々は疑いを見下すし続ける場合は、実装置きます。

アドレッシングアルゴリズムHash'Map後JDK1.8

私たちは、この方法を入れて返すのHashMap

    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

私たちは、このコードは少し長い方法putVal呼び出すことがわかります、私たちは少しを分析します

    /**
     * Implements Map.put and related methods.
     *  实现Map.put和相关的方法
     * @param hash key的hash值(这里是前面hash()方法算出来的值)
     * @param key put的key
     * @param value put的value
     * @param onlyIfAbsent 如果为true的话,不改变现有的值
     * @param evict 如果为false,则表处于创建模式。
     * @return previous value, or null if none
     */
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        // 声明节点类数组tab 节点p 数组长度n 键值对存放位置i
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        // 如果没有初始化table,则初始化,长度默认设置为16 --> n
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        // 这里就声明了我们put的键值对应该存放在数组的什么位置  
        // p = tab[i = (n - 1) & hash]) i = 数组位置
        // 如果这个数组下标对应的是null,就直接新建node写入到这个位置上,否则就得组织链表或红黑树了
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

ソースからハッシュマップに見出すことができるアルゴリズムをアドレス指定する際に置く式である:タブ[Iが= (N - 1)&ハッシュ ])

臭いませんここで疑問が生じ、なぜ、これほど多くあるのhashCodeと直接法としますか?ここであれば式をスローするようにnは2の累乗で、その後、hasCode%= n(nは- 1)&ハッシュ、我々はテストすることができ、あなたがに興味があるかもしれないならば、あなた自身のため見ることができます:

    public static void main(String[] args) {

        String[] strings = new String[16];
        String key = "qq1";
        int hash = key.hashCode();
        int length = strings.length;
        System.out.println(hash % length);
        System.out.println((length - 1) & hash);

    }
    // out      true
    
    public static void main(String[] args) {

        String[] strings = new String[64];
        String key = "zxl1";
        int hash = key.hashCode();
        int length = strings.length;
        System.out.println((hash % length) == ((length - 1) & hash));
    }
    // out      true

問題があったことを、なぜそれの配列のモジュロ長へのhashCodeの道はないのですか?のは、簡単なテストをやってみましょう

    public static void main(String[] args) {

        String[] strings = new String[64];
        String key = "zxl1";
        int hash = key.hashCode();
        int length = strings.length;
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 100000000; i++) {
            int k = hash % length;
        }
        System.out.println("----取模算法耗时 : " + (System.currentTimeMillis() - startTime) + "ms ----");
        startTime = System.currentTimeMillis();
        for (int i = 0; i < 100000000; i++) {
            int k = (length - 1) & hash;
        }
        System.out.println("----与运算耗时 : " + (System.currentTimeMillis() - startTime) + "ms ----");
    }

/**
* output
* ----取模算法耗时 : 2ms ----
* ----与运算耗时 : 1ms ----
*/

モジュロ演算に対して、より高い動作効率の使用は、コンピューティングに関与するアルゴリズムとJDKのハッシュマップアルゴリズムのアドレッシング。

ハッシュのハッシュマップではなく、直接ハッシュコードああを使用するよりも、自分の操作の結果である理由の使用及び操作は、しかし、言わなかった理由だけで、ここで言う、多くの学生が言うここを参照してください。実際には、これと(N - 1)・ハッシュに関連します。ここでは、例がある上に引用取ります:

    hashCode
    1111 1111 1111 1111 1001 1101 1100 0011
    hashCode右移16位
    0000 0000 0000 0000 1111 1111 1111 1111
    异或运算后的结果
    1111 1111 1111 1111 0110 0010 0011 1100

我々は直接N-1(16の既定の長さを仮定して)実行し、操作にハッシュコード(「1」に設定両方のビット、結果は「1」、そうでない場合は0であった)場合、結果はそのようなものです

1111 1111 1111 1111 1001 1101 1100 0011
0000 0000 0000 0000 0000 0000 0000 1111
0000 0000 0000 0000 0000 0000 0000 0011

[OK]を、私たちは3でなければならない配列で、このキーの位置を取得することができ、その後の質問は、私たちはハッシュコード16の後にキーを持っているし、我々は今、キーのハッシュコードは正確に同じですが、16元とは若干異なっていると仮定しています例えば

1111 1111 1001 1010 1001 1101 1100 0011
0000 0000 0000 0000 0000 0000 0000 1111
0000 0000 0000 0000 0000 0000 0000 0011

我々は、それが3で、2つの完全に矛盾ハッシュコード計算結果は同じである見つけました。その理由は、操作中に、16の前に役割を果たしていなかったので、それが結果一致しているの多くにつながるです。

ハッシュ方式の値は、あなたの中の右16ビットのハッシュコードの欲しい理由目的はフロント16と背面16が操作に関与しているようにすることですので、これは、その後、XORの予算説明できるかもしれません。

それとも、たとえば、間の差で見てみましょう:

1111 1111 1111 1111 1001 1101 1100 0011             hashcode
0000 0000 0000 0000 1111 1111 1111 1111             hashcode右移16位
1111 1111 1111 1111 0110 0010 0011 1100             hash算法结果
0000 0000 0000 0000 0000 0000 0000 1111             n-1
0000 0000 0000 0000 0000 0000 0000 1100             与预算结果=12

----------------------------------------------------------------------------------
1111 1111 1001 1010 1001 1101 1100 0011             hashcode
0000 0000 0000 0000 1111 1111 1001 1010             hashcode右移16位
1111 1111 1001 1010 0110 0010 0111 1101             hash算法结果
0000 0000 0000 0000 0000 0000 0000 1111             n-1
0000 0000 0000 0000 0000 0000 0000 1101             与预算结果=13

これは、なりますできるだけ均一にアレイ上に分散異なるハッシュコードを確保します。


継続するには

おすすめ

転載: www.cnblogs.com/joimages/p/12015702.html