LinkedHashMap继承了HashMap,实现了Map接口。由此可以看出LinkedHashMap是一个HashMap,推理出其在HashMap的基础上增加了某些东西。
成员变量
/**
* 双链接列表中的头(最年长的)。
*/
transient LinkedHashMap.Entry<K,V> head;
/**
* 双链接列表的尾部(最年轻的)。
*/
transient LinkedHashMap.Entry<K,V> tail;
/**
* 此链接哈希映射的迭代排序方法:<tt>true</tt>
* 对于访问顺序,<tt>false</tt>对于插入顺序。
*(即存键值对进去是什么顺序,迭代打印出来的就是什么顺序)。
* @serial
*/
final boolean accessOrder;
构造方法
因为LinkedHashMap继承了HashMap,所以两者底层架构并没有很大的差异(同样都是数组+链表+红黑树)。
两者的差别就在:LinkedHashMap的数据节点多了2个指针:头指针head和尾指针tail
它有5个构造方法,比HashMap多了1个,其他的四个构造方法和HashMap的都差不多
LinkedHashMap(): 无参构造,调用父类HashMap的无参构造(初始容量为16,负载因子为0.75),并且设置accessOrder=false;(即使用插入顺序)
/**
* 构造一个空的插入顺序<tt>LinkedHashMap</tt>实例
* 默认初始容量(16)和负载系数(0.75)。
*/
public LinkedHashMap() {
super();
accessOrder = false;
}
LinkedHashMap(int initialCapacity): 调用父类HashMap(initialCapacity),指定初始容量,并且设置accessOrder=false;(即使用插入顺序)
/**
* 构造一个空的插入顺序<tt>LinkedHashMap</tt>实例
* 具有指定的初始容量和默认负载系数(0.75)。
*/
public LinkedHashMap(int initialCapacity) {
super(initialCapacity);
accessOrder = false;
}
LinkedHashMap(int initialCapacity, float loadFactor): 调用父类HashMap(initialCapacity, loadFactor),指定初始容量和负载因子,并且设置accessOrder=false;(即使用插入顺序)
/**
*
* 构造具有指定初始容量和负载因子的空插入顺序LinkedHashMap实例。
*/
public LinkedHashMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
accessOrder = false;
}
LinkedHashMap(Map<? extends K, ? extends V> m): 调用父类HashMap的空参构造,并且设置accessOrder=false;(即使用插入顺序),调用putMapEntries()方法将map集合转为LinkedHashMap集合。容量由入参map集合决定
/**
* 使用与指定映射相同的映射构造插入顺序的LinkedHashMap实例。
* LinkedHashMap实例是使用默认负载因子(0.75)和初始容量创建的,
* 初始容量足以在指定的映射中保存映射。
*
* @param m the map whose mappings are to be placed in this map
* @throws NullPointerException if the specified map is null
*/
public LinkedHashMap(Map<? extends K, ? extends V> m) {
super();
accessOrder = false;
putMapEntries(m, false);
}
LinkedHashMap(int initialCapacity,float loadFactor,boolean accessOrder): 调用父类HashMap的(initialCapacity, loadFactor),并且设置指定的accessOrder(即使用指定的顺序)。此三个入参的构造方法在HashMap并没有
/**
* 使用指定的初始容量、
* 负载因子和排序模式构造一个空的LinkedHashMap实例。
*
* @param initialCapacity the initial capacity
* @param loadFactor the load factor
* @param accessOrder the ordering mode - <tt>true</tt> for
* access-order, <tt>false</tt> for insertion-order
* @throws IllegalArgumentException if the initial capacity is negative
* or the load factor is nonpositive
*/
public LinkedHashMap(int initialCapacity,
float loadFactor,
boolean accessOrder) {
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder;
}
静态内部类
这是增加了head、tail两个指针的类型,继承了HashMap.Node
//普通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);
}
}
linkNodeLast()方法
// link at the end of list
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;
}
}
访问顺序
- HashMap插入的顺序与取出的顺序是不一样的,即HashMap是无序的
- LinkedHashMap插入的顺序与取出的顺序是一样的,即LinkedHashMap是有序的
开启accessOrder访问顺序(即accessOrder=true)
可以看到被访问后的元素,被置顶了。也就是说,LinedHashMap访问某元素,某元素将被置顶。这样的功能应用在LRU缓存方面。 经常访问的元素就可以很快速地找到了,而不被经常访问的元素可以沉到底甚至可以被覆盖,LinkedHahsMap这样的功能使得它在缓存技术方面发扬光大。
总结
从上面的访问顺序,也可以总结出,遍历LinkedHashMap是按照从链表开始扫描,扫到尾部的。
get元素后,元素被放到链表的尾部,所以每次get后再遍历LinkedHashMap,get的元素都会出现在末尾。