Java 8 HashMap

3973 ワード

HashMapは、配列、チェーンテーブル、および赤黒ツリーを使用してキー値ペアを格納し、チェーンテーブルが十分に長い場合は赤黒ツリーに変換します.HashMapは非スレッドで安全です.
HashMapの定数
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
static final int MAXIMUM_CAPACITY = 1 << 30;
static final float DEFAULT_LOAD_FACTOR = 0.75f;
static final int TREEIFY_THRESHOLD = 8;
static final int UNTREEIFY_THRESHOLD = 6;
static final int MIN_TREEIFY_CAPACITY = 64;
  • DEFAULT_INITIAL_CAPACITY初期容量は16である.
  • MAXIMUM_CAPACITYの最大容量は230です.
  • DEFAULT_LOAD_FACTORデフォルトの充填係数.初期の場合、キー値ペア数が16*充填因子より大きいと、元の2倍に拡張されます.
  • TREEIFY_THRESHOLDチェーンテーブルの長さがこの値に達すると、ツリーに変換される可能性があります.
  • UNTREEIFY_THRESHOLDチェーンテーブル長がこの値より小さい場合、ツリーからチェーンテーブルに劣化します.
  • MIN_TREEIFY_CAPACITYツリーに移行する前に、キー値ペアの数がこの値より大きい場合にのみ赤黒ツリーに移行し、その値より小さい場合には拡張のみがトリガーされると判断します.

  • HashMapの容量はシフト動作に用いられ,1個の数aをnビット左にシフトすることは,a=a*2 nに相当するので,1<<4=>1*24=16である.したがって、HashMapの容量は常に2のn乗である.
    パラメトリック構造法を使用して、初期容量と充填係数を指定します.指定した容量は2のn乗に上方に調整されます(たとえば、指定した容量が13の場合、16に調整されます).
    HashMapのキー値ペアの値はnullであってもよく、keyがnullのキー値ペアが存在してもよい.
    HashMapの一部の方法
    treeifyBinメソッド
    /**
     * Replaces all linked nodes in bin at index for given hash unless
     * table is too small, in which case resizes instead.
     */
    final void treeifyBin(Node[] tab, int hash) {
        int n, index; Node e;
        //              
        if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
            resize();   //           
        else if ((e = tab[index = (n - 1) & hash]) != null) {
            TreeNode hd = null, tl = null;
            do {
                TreeNode p = replacementTreeNode(e, null);
                if (tl == null)
                    hd = p;
                else {
                    p.prev = tl;
                    tl.next = p;
                }
                tl = p;
            } while ((e = e.next) != null);
            if ((tab[index] = hd) != null)
                hd.treeify(tab);
        }
    }
    putメソッドを呼び出してキー値ペアを追加すると、数がTREEIFY_THRESHOLDに達するとtreeifyBinメソッドが呼び出され、このメソッドはテーブル長がMIN_TREEIFY_CAPACITYに達するかどうかを再判断し、達成されなければ拡張操作のみを行い、そうでなければテーブルをツリーに変換します.
    ここでの(n−1)&hashはhash%nに相当する余剰演算であり,HashMapの長さは常に2のn乗であるからである.
    replaceAllメソッド
    @Override
    public void replaceAll(BiFunction super K, ? super V, ? extends V> function) {
        Node[] tab;
        if (function == null)
            throw new NullPointerException();
        if (size > 0 && (tab = table) != null) {
            int mc = modCount;
            for (int i = 0; i < tab.length; ++i) {
                for (Node e = tab[i]; e != null; e = e.next) {
                    e.value = function.apply(e.key, e.value);
                }
            }
            if (modCount != mc)
                throw new ConcurrentModificationException();
        }
    }

    このメソッドは、lambda式を受け入れ、所定の条件を満たす値を置き換えます.たとえば、次のようにします.
    HashMap map = ...;
    //   key                "foo"
    map.replaceAll((k, v) -> k % 2 == 0 ? "foo" : v);

    forEachメソッド
    @Override
    public void forEach(BiConsumer super K, ? super V> action) {
        Node[] tab;
        if (action == null)
            throw new NullPointerException();
        if (size > 0 && (tab = table) != null) {
            int mc = modCount;
            for (int i = 0; i < tab.length; ++i) {
                for (Node e = tab[i]; e != null; e = e.next)
                    action.accept(e.key, e.value);
            }
            if (modCount != mc)
                throw new ConcurrentModificationException();
        }
    }

    この方法は、キー値ペアを消費するためのlambda式も受け入れます.
    //        
    map.forEach(
        (k, v) -> System.out.println(k + ": " + v)
    );
    
    //      key        
    map.forEach(
        (k, v) -> {
            if (k % 2 == 0)
                System.out.println(k + ": " + v)
        }
    );