1. 继承关系
LinkedHashMap继承了HashMap类和Map接口。与HashMap的不同之处在于,LinkedHashMap内部保存了一个双链表,这个双链表将所有的key-value对按照加入顺序或者访问顺序连接起来。并且,这个双链表的顺序,也决定了对该LinkedHashMap的迭代顺序。
public class LinkedHashMap<K,V>
extends HashMap<K,V>
implements Map<K,V>
LinkedHashMap不是线程安全的类,如果需要在多线程环境下使用,应该在外部做好线程同步。
2.成员属性
(1)节点结构
LinkedHashMap的节点类继承了HashMap中的Node类,新增了两个指针,一个指向前驱节点,一个指向后继节点。
static class Entry<K,V> extends HashMap.Node<K,V> {
//前驱指针和后继指针
Entry<K,V> before, after;
//节点的构造器,调用了HashMap.Node类的构造器
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}
(2)双链表的头指针
transient LinkedHashMap.Entry<K,V> head;
(3)双链表的尾指针
transient LinkedHashMap.Entry<K,V> tail;
(4)accessOrder
如果accessOrder为true,则把访问过的元素放在链表后面,放置顺序是访问的顺序。此时,最近最少访问(least-recently)的元素会置于链表前面,最近访问较多(most-recently)的元素会置于链表后面。这也使得LinkedHashMap非常适用于建立LRU缓存。
如果accessOrder为false,则按插入顺序来遍历。
final boolean accessOrder;
3. 构造器
(1)空参构造器,使用了默认的初始容量16和装载因子0.75。accessOrder设置为false,表示遍历顺序为元素插入的顺序。
public LinkedHashMap() {
super();
accessOrder = false;
}
(2)设置初始容量的构造器,装载因子默认0.75,accessOrder设置为false。
public LinkedHashMap(int initialCapacity) {
super(initialCapacity);
accessOrder = false;
}
(3)设置初始容量和装载因子的构造器,accessOrder设置为false。
public LinkedHashMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
accessOrder = false;
}
(4)传入一个map的构造器,将该map中所有的元素都加入到新创建的map中,accessOrder设置为false。
public LinkedHashMap(Map<? extends K, ? extends V> m) {
//先建一个空的map
super();
accessOrder = false;
//再把参数map中的所有元素都加入到新map中,该方法是HashMap中的方法
putMapEntries(m, false);
}
当调用putMapEntries方法将参数容器m中所有的元素添加到本容器中时,使用了foreach循环,来获取m中的所有键值对。而foreach循环是基于while循环和迭代器的,所以应该按照m所遵循的迭代规则,依次取得元素,放入本容器中。
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();
//使用了foreach循环
for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
K key = e.getKey();
V value = e.getValue();
//放置节点时,会按照插入顺序,进行链表连接,在第5节进行具体说明
putVal(hash(key), key, value, false, evict)