一、结构
public class LinkedHashMap<K,V>
extends HashMap<K,V>
implements Map<K,V>
{
LinkedHashMap继承于HashMap,不过其不同的是在遍历的时候LinkedHashMap是可以按添加的顺序去遍历,而HashMap我们通过前面的梳理可以知道其是按数组&链表去遍历,并不能按添加的顺序去遍历,这里的关键是LinkedHashMap去等待Node节点添加了两个变量before&after用来表明这个节点前面的节点与后面的节点。当增删改查的时候通过HashMap对应的3个空方法,去进行对应节点的整理,这部分的整理其是是类似与我们前面梳理的LinkeList。
// Callbacks to allow LinkedHashMap post-actions
void afterNodeAccess(Node<K,V> p) { }
void afterNodeInsertion(boolean evict) { }
void afterNodeRemoval(Node<K,V> p) { }
二、变量
1、head
transient LinkedHashMap.Entry<K,V> head;
这个表示头节点。
2、tail
transient LinkedHashMap.Entry<K,V> tail;
尾结点。
3、accessOrder
final boolean accessOrder;
这个是用于迭代遍历的顺序:如果为true,表明的是按存取顺序,为false表明的是插入顺序。整理怎么理解呢,例如我们前面梳理HashMap的putVal方法,在添加的时候可能已经存在了这个元素,所以本次是去覆盖原来的value,这时候算一种存取顺序,如果还没有这个元素,算是一种插入顺序。在存取顺序accessOrder = true
中,会将该节点重新将其从原来的位置插入到最后面。而如果是插入顺序,并不会将这个元素调整到最后,还是在原来的位置。
三、构造方法
1、LinkedHashMap(int initialCapacity, float loadFactor)
public LinkedHashMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
accessOrder = false;
}
其是直接调用的super方法初始化,同时accessOrder默认为false,表明是插入顺序。
2、LinkedHashMap(int initialCapacity)
public LinkedHashMap(int initialCapacity) {
super(initialCapacity);
accessOrder = false;
}
调用的父类方法。
3、LinkedHashMap()
public LinkedHashMap() {
super();
accessOrder = false;
}
4、HashMap(Map<? extends K, ? extends V> m)
public LinkedHashMap(Map<? extends K, ? extends V> m) {
super();
accessOrder = false;
putMapEntries(m, false);
}
这些构造方法可以看到其是调用的父类方法,其主要是将accessOrder设置为默认的false。
四、内部类
1、Entry
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);
}
}
继承HashMap.Node,其主要是在原继承上增加了before&after节点来表明本节点的上一个节点及下一个节点,借此就能实现按插入的顺序去遍历了。
2、LinkedKeySet
final class LinkedKeySet extends AbstractSet<K> {
public final int size() { return size; }
public final void clear() { LinkedHashMap.this.clear(); }
public final Iterator<K> iterator() {
return new LinkedKeyIterator();
}
public final boolean contains(Object o) { return containsKey(o); }
public final boolean remove(Object key) {
return removeNode(hash(key), key, null, false, true) != null;
}
......
}
与HashMap的没有什么主要 的区别。
3、LinkedValues
final class LinkedValues extends AbstractCollection<V> {
public final int size() { return size; }
public final void clear() { LinkedHashMap.this.clear(); }
public final Iterator<V> iterator() {
return new LinkedValueIterator();
}
public final boolean contains(Object o) { return containsValue(o); }
......
}
其他的LinkedEntrySet这些就不再提了。
5、LinkedHashIterator
1)、结构&成员变量&构造方法
abstract class LinkedHashIterator {
LinkedHashMap.Entry<K,V> next;
LinkedHashMap.Entry<K,V> current;
int expectedModCount;
LinkedHashIterator() {
next = head;
expectedModCount = modCount;
current = null;
}
这个就是LinkedHashMap实现的遍历迭代器。next表示下一个节点,current表示当前遍历迭代节点。同时这里是以head首节点为next起点,而HashMap这里是需要遍历数组按顺序看第一个不为空的index是哪个再赋值给next。
2)、方法
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();
if (e == null)
throw new NoSuchElementException();
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;
removeNode(p.hash, p.key, null, false, false);
expectedModCount = modCount;
}
可以看到这里的nextNode是通过取e
也就是next
的after节点作为下一个next
,这里与HashMap的遍历也是不同的。
6、LinkedKeyIterator
final class LinkedKeyIterator extends LinkedHashIterator
implements Iterator<K> {
public final K next() { return nextNode().getKey(); }
}
迭代器对节点的key遍历获取。
7、LinkedValueIterator
final class LinkedValueIterator extends LinkedHashIterator
implements Iterator<V> {
public final V next() { return nextNode().value; }
}
迭代器对节点的value遍历获取。
8、LinkedEntryIterator
final class LinkedEntryIterator extends LinkedHashIterator
implements Iterator<Map.Entry<K,V>> {
public final Map.Entry<K,V> next() { return nextNode(); }
}
对节点(Entry | Node)进行遍历,可以看到其只是将nextNode包装为next方法。
这几个与前面的HashMap是类似的。
五、方法
下面我们做来看下其对HashMap的afterNodeAccess&afterNodeInsertion&afterNodeRemoval这三个方法的实现。
1、afterNodeInsertion(boolean evict)
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);
}
}
这个是在传入元素后调用,例如:
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
......
afterNodeInsertion(evict);
return null;
}
然后这个方法还有另外一个功能,就是移除最早添加的元素也就是head节点。要移除其是有两个判断决定的即入参&removeEldestEntry方法,这个方法在LinkedHashMap总是返回false就是不执行删除首节点的操作
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
return false;
}
所以需要自己去重写,这里看JDK的介绍,例如你只想再Map中存储100个节点,就可以重写这个方法来判断删除首节点。
2、afterNodeRemoval(Node<K,V> 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;
}
这个方法就是在进行删除操作后调用的方法,例如:
final Node<K,V> removeNode(int hash, Object key, Object value,
boolean matchValue, boolean movable) {
......
afterNodeRemoval(node);
return node;
......
}
其的操作类似与LinkedList的操作,连接e
也就是本次删除的元素,将其前节点与后节点想连,也就删除了e
节点本身。
3、afterNodeAccess(Node<K,V> e)
void afterNodeAccess(Node<K,V> e) { // move node to last
LinkedHashMap.Entry<K,V> last;
if (accessOrder && (last = tail) != e) {
LinkedHashMap.Entry<K,V> p =
(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
p.after = null;
if (b == null)
head = a;
else
b.after = a;
if (a != null)
a.before = b;
else
last = b;
if (last == null)
head = p;
else {
p.before = last;
last.after = p;
}
tail = p;
++modCount;
}
}
这里就是我们前面解释的accessOrder
的使用,其的调用例如:
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
......
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
......
}
已经存在了原来的节点e
,就调用afterNodeAccess方法。同时accessOrder为true,表明使用存取顺序。就将本节点e
给移到最后面去。其的操作时将p
也就是e
的前后节点相连,然后再将p
设置为尾节点tail = p
。
4、newNode(int hash, K key, V value, Node<K,V> e)
Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
LinkedHashMap.Entry<K,V> p =
new LinkedHashMap.Entry<>(hash, key, value, e);
linkNodeLast(p);
return p;
}
这里就是创建LinkedHashMap的新节点。其主要是调用linkNodeLast方法来将建立的新节点添加到最后。
5、linkNodeLast(LinkedHashMap.Entry<K,V> p)
private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
LinkedHashMap.Entry<K,V> last = tail;
tail = p;
if (last == null)
head = p;
else {
p.before = last;
last.after = p;
}
}
设置p
节点为tail
尾节点,然后将tail
尾节点设置为p
的前置节点,将p
设置为tail
的后置节点。
5、get(Object key)
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;
}
获取key对应的value,可以看到在获取的时候,如果accessOrder
为true,也是会将查询到的节点移到最后面。这里其实与前面提到的afterNodeInsertion
方法再重写removeEldestEntry
方法其实就能实现一个按"最近最少使用"原则去删除节点了。
6、containsValue
public boolean containsValue(Object value) {
for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) {
V v = e.value;
if (v == value || (value != null && value.equals(v)))
return true;
}
return false;
}
看是否含有此value,可以看到其的遍历是用的head
及e.after
遍历。