概述
LinkedHashMap
实现了Map接口,即允许放入key为null的元素,也允许插入value为null的元素。从名字上可以看出该容器是LinkedList
和HashMap
的混合体,也就是说它同时满足HashMap
和LinkedList
的某些特性。可将LinkedHashMap
看作采用LinkedList
增强的HashMap
。
特点
- LinkedHashMap LinkedHashMap 继承自 HashMap,所以它的底层仍然是基于拉链式散列结构即由数组和链表或红黑树组成
- LinkedHashMap 在上面结构的基础上,增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序
- 由于继承自HashMap,所以依旧是线程不安全的
出于性能原因,_LinkedHashMap_是非同步的(not synchronized),如果需要在多线程环境使用,需要手动同步;或者通过如下方式将_LinkedHashMap_包装成(wrapped)同步的:
Map m = Collections.synchronizedMap(new LinkedHashMap(...));
- Collections.synchronizedMap 如何实现 Map 线程安全?
- 基于 Synchronized ,实际上就是锁住了当前传入的 Map 对象。
LinkedHashMap
是HashMap
的直接子类,二者唯一的区别是LinkedHashMap
在HashMap
的基础上,采用双向链表的形式将所有entry连接起来,主体部分跟_HashMap_
完全一样,多了header
指向双向链表的头部(是一个哑元),该双向链表的迭代顺序就是entry的插入顺序,这样是为保证元素的迭代顺序跟插入顺序相同。
方法分析
构造函数
public LinkedHashMap() {
//调用父类的构造器使用默认容量16与默认负载因子0.75
super();
accessOrder = false;
}
public LinkedHashMap(int initialCapacity) {
//调用父类的构造器设置初始大小和使用默认负载因子0.75
super(initialCapacity);
accessOrder = false;
}
public LinkedHashMap(int initialCapacity, float loadFactor) {
//调用父类构造器设置初始大小和负载因子,
super(initialCapacity, loadFactor);
accessOrder = false;
}
public LinkedHashMap(int initialCapacity, float loadFactor,boolean accessOrder) {
//调用父类构造器设置初始大小和负载因子
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder;
}
//将集合m中的元素拷贝到当前LinkedHashMap中
public LinkedHashMap(Map<? extends K, ? extends V> m) {
super();
accessOrder = false;
putMapEntries(m, false);
}
get(K key)
get(Object key)方法根据指定的key值返回对应的value。该方法跟HashMap.get()方法的流程几乎完全一样
put(K key, V value)方法是将指定的key, value对添加到map里。
该方法首先会对map做一次查找,看是否包含该元组,如果已经包含则直接返回,查找过程类似于get()方法;如果没有找到,则会通过addEntry(int hash, K key, V value, int bucketIndex)方法插入新的entry。
注意,这里的插入有两重含义:
- 从table的角度看,新的entry需要插入到对应的bucket里,当有哈希冲突时,采用头插法将新的entry插入到冲突链表的头部。
- 从header的角度看,新的entry需要插入到双向链表的尾部。
remove()
remove(Object key)的作用是删除key值对应的entry,该方法的具体逻辑是在removeEntryForKey(Object key)里实现的。removeEntryForKey()方法会首先找到key值对应的entry,然后删除该entry(修改链表的相应引用)。查找过程跟get()方法类似。
注意,这里的删除也有两重含义:
- 从table的角度看,需要将该entry从对应的bucket里删除,如果对应的冲突链表不空,需要修改冲突链表的相应引用。
- 从header的角度来看,需要将该entry从双向链表中删除,同时修改链表中前面以及后面元素的相应引用。