继承关系
通过上图可以看出LinkedHashMap
直接继承了HashMap
接口,实现了Map
接口,间接实现了Cloneable
和Serializable
接口
构造方法
LinkedHashMap
拥有四个构造方法,由于直接继承了HashMap
,具体实现由此父类决定
在这几个构造方法之前存在一个重要的属性accessOrder
,其决定了在迭代LinkedHashMap
时是以访问顺序还是以插入顺序进行
/**
* The iteration ordering method for this linked hash map: true
* for access-order, false for insertion-order.
*
* @serial
*/
final boolean accessOrder;
在HashMap
中Node
是用来存储数据的,在LinkedHashMap
中对此结构进行了拓展,新增了before和after,以双向链表的形式存储数据
static class Entry extends HashMap.Node {
// 双向链表的前驱和后继
Entry before, after;
Entry(int hash, K key, V value, Node next) {
// 调用HashMap中Node的构造方法
super(hash, key, value, next);
}
}
LinkedHashMap()
// 调用了HashMap的无参构造方法,并设置为以插入顺序进行迭代
public LinkedHashMap() {
super();
accessOrder = false;
}
LinkedHashMap(int initialCapacity)
// 设置Map的的初始值,并由HashMap具体实现
public LinkedHashMap(int initialCapacity) {
super(initialCapacity);
accessOrder = false;
}
LinkedHashMap(int initialCapacity, float loadFactor)
// 设置Map的初始值和加载因子
public LinkedHashMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
accessOrder = false;
}
LinkedHashMap(Map extends K, ? extends V> m)
// 合并两个Map,调用父类的putMapEntries()方法进行实现
public LinkedHashMap(Map extends K, ? extends V> m) {
super();
accessOrder = false;
putMapEntries(m, false);
}
添加数据
在对LinkedHashMap
执行put()方法时,实际上是调用HashMap
的put()方法,由于在上篇文章中已经说过这部分代码,这里只对关键代码进行解释
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {
HashMap.Node[] tab;
HashMap.Node p;
int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
// 进行第一次初始化
n = (tab = resize()).length;
// 判断是否存在当前元素
if ((p = tab[i = (n - 1) & hash]) == null)
// 不存在则进行插入,这里利用多态性调用LinkedHashNap的newNode()方法
tab[i] = newNode(hash, key, value, null);
else {
...
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
调用newNode()
Node newNode(int hash, K key, V value, Node e) {
// 创建一个新节点
LinkedHashMap.Entry p = new LinkedHashMap.Entry(hash, key, value, e);
linkNodeLast(p);
// 并返回当前元素
return p;
}
private void linkNodeLast(LinkedHashMap.Entry p) {
// tail表示双向链表最末尾的元素;在进行第一次添加时tail为null,先把tail赋值给last节点,此时last也为Null
LinkedHashMap.Entry last = tail;
// 将新元素添加到队列末尾
tail = p;
// 当第一次添加数据时,将第一个元素设置为头节点
if (last == null)
head = p;
else {
// 若不是第一次添加
// 修改当前元素的前驱指向
p.before = last;
// 修改最后一个元素的后继指向
last.after = p;
}
}
至此,向Map中添加新元素的工作已经完成。在这个过程中,数据存储在Node数组中,Entry保存了插入顺序。
获取数据
获取数据主要由HashMap
实现,这部分内容已经在上篇文章中讲过,不做赘述
public V get(Object key) {
Node e;
// 通过HashMap的getNode()方法获取数据
if ((e = getNode(hash(key), key)) == null)
return null;
// 如果为true 将获取到的元素e放到末尾
if (accessOrder)
afterNodeAccess(e);
// 获取值
return e.value;
}
总结
通过LinkedHashMap
源码分析发现,该数据类型是通过内部的Entry记录数据的插入顺序并组成双向链表。底层存储数据的依旧是数组、单向链表和红黑树。也发现了与HashMap
存在的异同。
相同点:
- 允许一条null Key的数据和多条null Value的数据
- 底层存储数据的结构不变
- 都是线程不安全的
不同点:
HashMap
是无序的,LinkedHashMap
是有序的