JavaのLinked HashMapソース解析

10678 ワード

概要:
Linked HashMapは、MapがhashMapを継承することを実現し、Mapのハッシュ・テーブルおよびチェーンに基づいて、このリストを実施し、予知可能な反復順序を有する。
LindedHashMapは、挿入またはアクセス順であることができる反復順序を定義するすべてのエントリを実行する二重リンク構造を維持している。
 LitHashMapのノードオブジェクトは、HashMapのノードオブジェクトを継承し、前後のポインタbefore afterを追加します。

/**
 * LinkedHashMap    
 */
 static class Entry<K,V> extends HashMap.Node<K,V> {
  Entry<K,V> before, after;
  Entry(int hash, K key, V value, Node<K,V> next) {
   super(hash, key, value, next);
  }
 }
lintHashMap初期化:
accessOrderは、簡単に言えば、これは元素の順序を制御するために使われます。
accessOrderはtrueです。訪問の順番で、つまり誰が一番先に訪問するかを表しています。
accessOrderはfalseとして格納順序によって、put元素の時の順番を表します。

public LinkedHashMap(int initialCapacity, float loadFactor) {
  super(initialCapacity, loadFactor);
  accessOrder = false;
 }

 /**
  *       LinkedHashMap,        ,         0.75,
  * accessOrder false         ,   put        
  * accessOrder true:           ,        ,      
  */
 public LinkedHashMap(int initialCapacity) {
  super(initialCapacity);
  accessOrder = false;
 }
 /**
  *       HashMap,         16,         0.75
  *    accessOrder  false,       .
  */
 public LinkedHashMap() {
  super();
  accessOrder = false;
 }
 /**
  *      map      HashMap,         ,       Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,DEFAULT_INITIAL_CAPACITY)
  *    accessOrder  false,       .
  */
 public LinkedHashMap(Map<? extends K, ? extends V> m) {
  super();
  accessOrder = false;
  putMapEntries(m, false);
 }
 /**
  *       LinkedHashMap,             ,
  *    accessOrder  true,       
  */
 public LinkedHashMap(int initialCapacity,
       float loadFactor,
       boolean accessOrder) {
  super(initialCapacity, loadFactor);
  this.accessOrder = accessOrder;
 }

putMapEnttries(m,false)は、親種類のHashMapを呼び出す方法で、次にHashMapのputに従ってデータの挿入を実現する。

 /**
  * Implements Map.putAll and Map constructor
  *
  * @param m the map
  * @param evict false when initially constructing this map, else
  * true (relayed to method afterNodeInsertion).
  */
 final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
  int s = m.size();
  if (s > 0) {
   if (table == null) { // pre-size
    float ft = ((float)s / loadFactor) + 1.0F;
    int t = ((ft < (float)MAXIMUM_CAPACITY) ?
       (int)ft : MAXIMUM_CAPACITY);
    if (t > threshold)
     threshold = tableSizeFor(t);
   }
   else if (s > threshold)
    resize();
   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);
   }
  }
 }
保存:
Putが呼び出したHashMapのputメソッドは、2つの空き方法を呼び出し、Linked HashMapによって実現されます。

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

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
     boolean evict) {
  Node<K,V>[] tab; Node<K,V> p; int n, i;
  if ((tab = table) == null || (n = tab.length) == 0)
   n = (tab = resize()).length;
  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;
 }
hashmapでは赤い部分が空です。

 void afterNodeAccess(Node<K,V> p) { }
 void afterNodeInsertion(boolean evict) { }
そしてLinked HashMapはどうやってこの2つの方法を実現しますか?
現在のノードeを双方向チェーンの末尾に移動します。Linked HashMapの中に元素が訪問されるたびに、訪問先順に並べられます。先に訪問したのは双方向チェーンの中で前にあり、後ほど訪問したのは尾部に近いです。もちろんaccessOrderがtrueである場合にのみ、この操作が実行されます。

void afterNodeAccess(Node<K,V> e) {
  LinkedHashMap.Entry<K,V> last;
  //       true,           
  if (accessOrder && (last = tail) != e) {
   //     ,  p     
   LinkedHashMap.Entry<K,V> p =
    (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
   // p      
   p.after = null;
   //   p      
   if (b == null)
    // a    
    head = a;
   else // p       
    // b     a
    b.after = a;
   // p       
   if (a != null)
    // a     b
    a.before = b;
   else // p      
    //           
    last = b;
   //          
   if (last == null)
    //     p
    head = p;
   else { // p          
    p.before = last;
    last.after = p;
   }
   //     p
   tail = p;
   //          
   ++modCount;
  }
 }
afterNodeInsertion方法evictがtrueの時に双方向チェーンのヘッドノードを削除します。

 void afterNodeInsertion(boolean evict) { // possibly remove eldest
  LinkedHashMap.Entry<K,V> first;
     //      ,     
  if (evict && (first = head) != null && removeEldestEntry(first)) {
   K key = first.key;
   removeNode(hash(key), key, null, false, true);
  }
 }
操作を削除してHashMapのremove方法を呼び出して要素の削除を実現して、removeはremoveNodeを呼び出して、removeNodeはLinked HashMapを必要として実現します:
eノードを双方向チェーンから削除し、e前後ノード参照関係を変更して、完全な双方向チェーンテーブルに再接続させる。

 void afterNodeRemoval(Node<K,V> e) { // unlink
  LinkedHashMap.Entry<K,V> p =
   (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
  p.before = p.after = null;
  if (b == null)
   head = a;
  else
   b.after = a;
  if (a == null)
   tail = b;
  else
   a.before = b;
 }
読み込み:
eが空でない場合は、eのvalue値を取得して戻ります。

public V get(Object key) {
  Node<K,V> e;
  if ((e = getNode(hash(key), key)) == null)
   return null;
  if (accessOrder)
   afterNodeAccess(e);
  return e.value;
 }
accessOrderはtrueであり、つまりアクセス順にコンテンツを取得する。

 void afterNodeAccess(Node<K,V> e) {
  LinkedHashMap.Entry<K,V> last;
  //       true,           
  if (accessOrder && (last = tail) != e) {
   //     ,  p     
   LinkedHashMap.Entry<K,V> p =
    (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
   // p      
   p.after = null;
   //   p      
   if (b == null)
    // a    
    head = a;
   else // p       
    // b     a
    b.after = a;
   // p       
   if (a != null)
    // a     b
    a.before = b;
   else // p      
    //           
    last = b;
   //          
   if (last == null)
    //     p
    head = p;
   else { // p          
    p.before = last;
    last.after = p;
   }
   //     p
   tail = p;
   //          
   ++modCount;
  }
 }
Linked HashMapのいくつかのディケンサ:
抽象的なLinked HashIteratorは具体的な削除を実現して、次の結点、反復の論理があるかどうかを判断します。
Linked KeyIteratorはLinked HashIteratorから引き継ぎ、Iteratorインターフェースを実現し、Linked HashMapのkeyを反復します。
Linked Value IteratorはLinked HashIteratorから引き継ぎ、Iteratorインターフェースを実現し、Linked HashMapのValueを反復します。
Linked EntryIteratorはLinked HashIteratorから継承され、Iteratorインターフェースを実現し、Linked HashMapの中の結点を反復します。

abstract class LinkedHashIterator {
  //     
  LinkedHashMap.Entry<K,V> next;
  //    
  LinkedHashMap.Entry<K,V> current;
  //       
  int expectedModCount;

  LinkedHashIterator() {
   //next      
   next = head;
   //      
   expectedModCount = modCount;
   //        
   current = null;
  }
  //         
  public final boolean hasNext() {
   return next != null;
  }

  final LinkedHashMap.Entry<K,V> nextNode() {
   LinkedHashMap.Entry<K,V> e = next;
   //           
   if (modCount != expectedModCount)
    throw new ConcurrentModificationException();
   //   null NoSuchElementException
   if (e == null)
    throw new NoSuchElementException();
   //  null,      
   current = e;
   //       
   next = e.after;
   return e;
  }
  //    
  public final void remove() {
   Node<K,V> p = current;
   if (p == null)
    throw new IllegalStateException();
   if (modCount != expectedModCount)
    throw new ConcurrentModificationException();
   current = null;
   K key = p.key;
   //      
   removeNode(hash(key), key, null, false, false);
   expectedModCount = modCount;
  }
 }

 final class LinkedKeyIterator extends LinkedHashIterator
  implements Iterator<K> {
  public final K next() { return nextNode().getKey(); }
 }

 final class LinkedValueIterator extends LinkedHashIterator
  implements Iterator<V> {
  public final V next() { return nextNode().value; }
 }

 final class LinkedEntryIterator extends LinkedHashIterator
  implements Iterator<Map.Entry<K,V>> {
  public final Map.Entry<K,V> next() { return nextNode(); }
 }

以上が本文の全部です。皆さんの勉強に役に立つように、私たちを応援してください。