LinkedHashMap类似于HashMap,但是迭代遍历它时,取得“键值对”的顺序是插入次序,或者是最近最少使用(LRU)的次序。只比HashMap慢一点;而在迭代访问时反而更快,因为它使用链表维护内部次序(HashMap是基于散列表实现的
//LinkedHashMap是继承HashMap的
public class LinkedHashMap<K,V>extends HashMap<K,V>
implements Map<K,V>
定义了两个属性:
//链表的头指针
private transient Entry<K,V> header;
//true表示最近最少使用次序(最近访问的在链表),false表示插入顺序
private final boolean accessOrder;
LinkedList一共提供了五个构造方法:
// 构造方法1,构造一个指定初始容量和负载因子的、按照插入顺序的LinkedList
public LinkedHashMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
accessOrder = false;
}
// 构造方法2,构造一个指定初始容量,按照插入顺序的LinkedList
public LinkedHashMap(int initialCapacity) {
super(initialCapacity);
accessOrder = false;
}
// 构造方法3,用默认的初始化容量和负载因子创建一个LinkedHashMap,取得键值对的顺序是插入顺序
public LinkedHashMap() {
super();
accessOrder = false;
}
// 构造方法4,通过传入的map创建一个LinkedHashMap,容量为默认容量(16)和(map.zise()/DEFAULT_LOAD_FACTORY)+1的较大者,装载因子为默认值
public LinkedHashMap(Map<? extends K, ? extends V> m) {
super(m);
accessOrder = false;
}
// 构造方法5,根据指定容量、装载因子和键值对保持顺序创建一个LinkedHashMap
public LinkedHashMap(int initialCapacity,
float loadFactor,
boolean accessOrder) {
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder;
}
在hashMap中,有一个空的init()方法,在这里就是出初始化链表头指针
@Override
void init() {
header = new Entry<>(-1, null, null, null);
header.before = header.after = header;
}
// 重写父类的containsValue(Object value)方法,直接通过header遍历链表判断是否有值和value相等,而不用查询table数组,速度相对慢点
public boolean containsValue(Object value) {
if (value==null) {
for (Entry e = header.after; e != header; e = e.after)
if (e.value==null)
return true;
} else {
for (Entry e = header.after; e != header; e = e.after)
if (value.equals(e.value))
return true;
}
return false;
}
get(Object key)方法通过HashMap的getEntry(Object key)方法获取节点,并返回该节点的value值,获取节点如果为null则返回null。
public V get(Object key) {
Entry<K,V> e = (Entry<K,V>)getEntry(key);
if (e == null)
return null;
//这个方法在:HashMap中为空,其实在这里就是将刚刚访问的节点,删除原来位置上的节点,然后重新插入链表的尾部,表示最近反问节点,可以模拟Cache
e.recordAccess(this);
return e.value;
}
清空 调用父类的方法,再将头结点为初始化状态
public void clear() {
super.clear();
header.before = header.after = header;
}
下面详细介绍LinkedHashMap的内部类Entry
private static class Entry<K,V> extends HashMap.Entry<K,V> {
//前驱节点和后继节点指向
Entry<K,V> before, after;
Entry(int hash, K key, V value, HashMap.Entry<K,V> next) {
//调用父类构造函数,初始化节点
super(hash, key, value, next);
}
// 移除该节点,只需修改前一节点的after引用和后一节点的before引用
private void remove() {
before.after = after;
after.before = before;
}
// 在指定节点之前插入当前节点(双向链表插入节点的过程)
private void addBefore(Entry<K,V> existingEntry) {
after = existingEntry;
before = existingEntry.before;
before.after = this;
after.before = this;
}
// 当调用此类的get方法或put方法(put方法将调用到父类HashMap.Entry的put
方法)都将调用到recordAccess(HashMap<K,V> m)方法如果accessOrder为true,即使用的是最近最少使用的次序,则将当前被修改的节点移动到header节点之前,即链表的尾部。这也是为什么在HashMap.Entry中有一个空的recordAccess(HashMap<K,V> m)方法的原因
void recordAccess(HashMap<K,V> m) {
LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
if (lm.accessOrder) { //LRU排列顺序
lm.modCount++;
remove();
addBefore(lm.header);
}
}
//和recordAccess(HashMap<K.V> m)方法一样,在HashMap.Entry中同样有一个对应的空方法。当进行删除(remove)操作的时候会被调用
void recordRemoval(HashMap<K,V> m) {
remove();
}
}
下面是创建一个Entry节点和添加一个Entry的两个方法。
void addEntry(int hash, K key, V value, int bucketIndex) {
//调用父类的addEntry方法
super.addEntry(hash, key, value, bucketIndex);
Entry<K,V> eldest = header.after;
if (removeEldestEntry(eldest)) {
removeEntryForKey(eldest.key);
}
}
//删除访问次数最少的节点,
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
//这里只返回false,表示不会删除该节点,我们可以模拟Cache,设置一个阈值,适当的时候,置换节点
return false;
}
void createEntry(int hash, K key, V value, int bucketIndex) {
//获得根据hash和key隐射到数组中的位置的节点,采用头插法插入节点
HashMap.Entry<K,V> old = table[bucketIndex];
Entry<K,V> e = new Entry<>(hash, key, value, old);
table[bucketIndex] = e;
e.addBefore(header); //在链尾添加节点
size++;
}
LinkedHashMap除了以上内容外还有和迭代相关的三个方法及三个内部类以及一个抽象内部类,
private class KeyIterator extends LinkedHashIterator<K> {
public K next() { return nextEntry().getKey(); }
}
private class ValueIterator extends LinkedHashIterator<V> {
public V next() { return nextEntry().value; }
}
private class EntryIterator extends LinkedHashIterator<Map.Entry<K,V>> {
public Map.Entry<K,V> next() { return nextEntry(); }
}
下面是LinkedHashIterator类的内容。
private abstract class LinkedHashIterator<T> implements Iterator<T> {
Entry<K,V> nextEntry = header.after;
Entry<K,V> lastReturned = null;
int expectedModCount = modCount;
public boolean hasNext() {
return nextEntry != header;
}
public void remove() {
if (lastReturned == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
LinkedHashMap.this.remove(lastReturned.key);
lastReturned = null;
expectedModCount = modCount;
}
Entry<K,V> nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
if (nextEntry == header)
throw new NoSuchElementException();
Entry<K,V> e = lastReturned = nextEntry;
nextEntry = e.after;
return e;
}
}