1.
LinkedHashMapはHashMapを継承し、基になるデータ構造はHashMapと同じであり、HashMapのすべての特性を備えています。違いは、LinkedHashMapが「挿入順のアクセス」と「最小アクセス要素戦略の削除」を追加したことです。
ソースコード
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>{
//链表头
transient LinkedHashMap.Entry<K,V> head;
//链表尾
transient LinkedHashMap.Entry<K,V> tail;
//继承Node,为数组的每个元素增加了before和after属性
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);
}
}
/**
* 控制两种访问模式的字段,默认为false
* true表示按照访问顺序把经常访问的key放到队尾
* false表示按照插入顺序访问
*/
final boolean accessOrder;
}
ソースコード分析
上記のマップの新しい属性からわかるように、LinkedHashMapのデータ構造は、LinkedListの要素をHashMapのノードに置き換えるのと非常によく似ています。これらの構造が追加されたため、Map要素を直列に接続してリンクリストを形成できます。リンクされたリストは、順序を保証し、要素の挿入の順序を維持できます。
2.
newNode / newTreeNodeメソッドを介して新しく追加されたLinkedHashMapは、ノードを追加します。以下では、ソースコードを分析するための例としてnewNodeを取り上げます。具体的なソースコードは、次のとおりです。
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>{
Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
//新增节点
LinkedHashMap.Entry<K,V> p = new LinkedHashMap.Entry<K,V>(hash, key, value, e);
//追加到链表的尾部
linkNodeLast(p);
return p;
}
private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
LinkedHashMap.Entry<K,V> last = tail;
//新增节点等于尾节点
tail = p;
//last为空,说明链表为空,首尾节点相等
if (last == null)
head = p;
//链表有数据,直接建立新增节点和上个尾节点之间的前后关系
else {
p.before = last;
last.after = p;
}
}
}
ソースコード分析
- LinkedHashMapが初期化されると、accessOrderはfalseになり、挿入順にアクセスが提供されます。挿入メソッドは親クラスHashMapのputメソッドを使用しますが、putメソッドは上書きされ、実行中にnewNode / newTreeNodeメソッドとafterNodeAccessメソッドが呼び出されます。
- LinkedHashMapは、ヘッドノードとテールノードを追加することにより、各ノードに前後の属性を追加します。新しいノードが追加されるたびに、そのノードがテールノードに追加されるため、新しいノードがリンクされたリストに順番に挿入されていることを確認できます。
3.アクセス
LinkedHashMapは、主にイテレーターを介してアクセスされます。イテレーターが初期化されると、デフォルトでヘッドノードからアクセスされます。イテレーション中は、現在のノードのアフターノードに引き続きアクセスできます。イテレーターの具体的なソースコードは次のとおりです。
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>{
abstract class LinkedHashIterator {
LinkedHashMap.Entry<K,V> next;
LinkedHashMap.Entry<K,V> current;
int expectedModCount;
//初始化时默认从头节点开始访问
LinkedHashIterator() {
//头节点作为第一个访问的节点
next = head;
expectedModCount = modCount;
current = null;
}
final LinkedHashMap.Entry<K,V> nextNode() {
LinkedHashMap.Entry<K,V> e = next;
//校验
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
if (e == null)
throw new NoSuchElementException();
current = e;
//通过链表的after结构,找到下一个迭代的节点
next = e.after;
return e;
}
}
}
ソースコード分析
- LinkedHashMapは一方向のアクセスのみを提供します。つまり、アクセスは挿入順に最初から最後まで実行され、LinkedListのように両方向にアクセスすることはできません。
- 新しいノードを追加するとき、要素間の挿入順序は維持されているため、繰り返しアクセスするときは、現在のノードの次のノードにアクセスし続けるだけで済みます。
4.最小削除戦略へのアクセス
この戦略はLRU(最近使用されていない、最も最近使用されていない)とも呼ばれます。つまり、頻繁にアクセスされる要素がチームの最後に追加されるため、頻繁にアクセスされないデータは自然にチームの先頭に近くなり、設定によって削除されます。戦略(マップ要素の数が特定のしきい値を超える場合など)、ヘッドノードを削除します。具体的なソースコードは次のとおりです。
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>{
public V get(Object key) {
Node<K,V> e;
//调用HashMap的get方法
if ((e = getNode(hash(key), key)) == null)
return null;
//如果设置了LRU策略,就把当前key移动到队尾
if (accessOrder)
afterNodeAccess(e);
return e.value;
}
//删除很少被访问的元素
void afterNodeInsertion(boolean evict) {
//得到元素头节点
LinkedHashMap.Entry<K,V> first;
//removeEldestEntry来控制删除策略,如果队列不为空,并且删除策略允许删除的情况下,删除头节点
if (evict && (first = head) != null && removeEldestEntry(first)) {
K key = first.key;
//删除头节点
removeNode(hash(key), key, null, false, true);
}
}
}
ソースコード分析
- getメソッドだけでなく、getOrDefault、compute、computeIfAbsent、computeIfPresent、およびmergeメソッドが実行されたときにも、afterNodeAccessメソッドを使用して、現在アクセスしているノードをキューの最後に移動します。頻繁にアクセスするノードをチームの最後に絶えず移動した後、チームのヘッドに近いノードは当然、めったにアクセスされない要素です。
- LinkedHashMap自体はputメソッドを実装していません。HashMapのputメソッドを呼び出しますが、LinkedHashMapは、削除操作を実装するputメソッドにafterNodeInsertionメソッドを実装します。