1、使用场景使用场景:当我们希望有顺序地去存储key-value时,就可以使用LinkedHashMap,LinkedHashMap是有序的,且默认为插入顺序2、构造方法3、双向链表的重排序4、LinkedHashMap特点LinkedHashMap是继承于HashMap,是基于HashMap和双向链表来实现的。LinkedHashMap有序,可分为插入顺序和访问顺序俩种。如果是访问顺序,那put个get操作已存在的Entry是,都会把Entry移动到双向链表的表尾(先删除在插入)。LinkedHashMap存取数据,还是跟HashMap一样使用Entry[]的方式,双向链表只是为了保证顺序。LinkedHashMap是线程不安全的。使用场景:当我们希望有顺序地去存储key-value时,就可以使用LinkedHashMap,LinkedHashMap是有序的,且默认为插入顺序继承结构:public class LinkedHashMap<K,V>extends HashMap<K,V>implements Map<K,V>实现了map接口,可以克隆,序列化基本属性与默认值// 双端链表的头元素 transient LinkedHashMap.Entry<K,V> head; // 双端链表的尾元素 transient LinkedHashMap.Entry<K,V> tail; // 默认为false,指的是添加顺序; // 当指定为true时,访问顺序, final boolean accessOrder; 构造方法: public LinkedHashMap(int initialCapacity, float loadFactor) { super(initialCapacity, loadFactor); accessOrder = false; } public LinkedHashMap(int initialCapacity) { super(initialCapacity); accessOrder = false; } public LinkedHashMap() { super(); accessOrder = false; } public LinkedHashMap(Map<? extends K, ? extends V> m) { super(); accessOrder = false; putMapEntries(m, false); } public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) { super(initialCapacity, loadFactor); this.accessOrder = accessOrder; } }首先使用super调用了父类HashMap的构造方法,其实就是根据初始容量、负载因子去初始化HashMap。然后把accessOrder设置为false,这就跟存储的顺序有关了,LinkedHashMap存储数据是有序的,而且分为两种:插入顺序和访问顺序。这里accessOrder设置为false,表示不是访问顺序而是插入顺序存储的,这也是默认值,表示LinkedHashMap中存储的顺序是按照调用put方法插入的顺序进行排序的。 只有调用了构造方法 LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) ,才能实现访问顺序排序。Entry 元素:LinkedHashMap 重新定义了数组中保存的元素 Entry:Entry 继承于 HashMap 中的 Node 元素,并且多了保存了其上一个元素before和下一个元素after的引用。 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); } } get 方法:首先调用父类 HashMap 中方法 getNode 寻找是否对应 key 的元素,并且赋值给临时变量 Node<K,V> e。如果 e 为 null,则直接返回 null。如果 e 不为空,则 判断 accessOrder 的值。如果为 false,则 直接返回 e 中的 value。如果 accessOrder 的值为 true,则把 e 作为参数,调用方法 afterNodeAccess(e)。 public V get(Object key) { Node<K,V> e; if ((e = getNode(hash(key), key)) == null) return null; if (accessOrder) afterNodeAccess(e); return e.value; }afterNodeRemoval()方法//在删除节点e时,同步将e从双向链表上删除 void afterNodeRemoval(Node<K,V> e) { // unlink LinkedHashMap.Entry<K,V> p = (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after; //待删除节点 p 的前置后置节点都置空 p.before = p.after = null; //如果前置节点是null,则现在的头结点应该是后置节点a if (b == null) head = a; else//否则将前置节点b的后置节点指向a b.after = a; //同理如果后置节点时null ,则尾节点应是b if (a == null) tail = b; else//否则更新后置节点a的前置节点为b a.before = b; }afterNodeAccess 方法 在afterNodeAccess()函数中,会将当前被访问到的节点e,移动至内部的双向链表的尾部。
void afterNodeAccess(Node<K,V> e) { // move node to last LinkedHashMap.Entry<K,V> last;//原尾节点 //如果accessOrder 是true ,且原尾节点不等于e if (accessOrder && (last = tail) != e) { //节点e强转成双向链表节点p LinkedHashMap.Entry<K,V> p = (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after; //p现在是尾节点, 后置节点一定是null p.after = null; //如果p的前置节点是null,则p以前是头结点,所以更新现在的头结点是p的后置节点a if (b == null) head = a; else//否则更新p的前直接点b的后置节点为 a b.after = a; //如果p的后置节点不是null,则更新后置节点a的前置节点为b if (a != null) a.before = b; else//如果原本p的后置节点是null,则p就是尾节点。 此时 更新last的引用为 p的前置节点b last = b; if (last == null) //原本尾节点是null 则,链表中就一个节点 head = p; else {//否则 更新 当前节点p的前置节点为 原尾节点last, last的后置节点是p p.before = last; last.after = p; } //尾节点的引用赋值成p tail = p; //修改modCount。 ++modCount; } }afterNodeAccess()函数中,会修改modCount,因此当你正在accessOrder=true的模式下,迭代LinkedHashMap时,如果同时查询访问数据,也会导致fail-fast,因为迭代的顺序已经改变。
containsValue方法它重写了该方法,相比HashMap的实现,更为高效。
public boolean containsValue(Object value) { //遍历一遍链表,去比较有没有value相等的节点,并返回 for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) { V v = e.value; if (v == value || (value != null && value.equals(v))) return true; } return false; }void init() { header = new Entry<>(-1, null, null, null); header.before = header.after = header;}
Put方法:
插入元素 put操作
//父类的实现
public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
if (key == null)
return putForNullKey(value);
int hash = hash(key);
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
//父类的实现
void addEntry(int hash, K key, V value, int bucketIndex) {
if ((size >= threshold) && (null != table[bucketIndex])) {
resize(2 * table.length);
hash = (null != key) ? hash(key) : 0;
bucketIndex = indexFor(hash, table.length);
}
createEntry(hash, key, value, bucketIndex);
}
//子类的实现
void createEntry(int hash, K key, V value, int bucketIndex) {
HashMap.Entry<K,V> old = table[bucketIndex];
Entry<K,V> e = new Entry<>(hash, key, value, old);
table[bucketIndex] = e;
e.addBefore(header);
size++;
}
//子类的实现
private void addBefore(Entry<K,V> existingEntry) {
after = existingEntry;
before = existingEntry.before;
before.after = this;
after.before = this;
}
//子类的实现
void addEntry(int hash, K key, V value, int bucketIndex) {
super.addEntry(hash, key, value, bucketIndex);
// Remove eldest entry if instructed
Entry<K,V> eldest = header.after;
if (removeEldestEntry(eldest)) {
removeEntryForKey(eldest.key);
}
}
属性:accessOrder true:按照访问顺序组织数据 false: 按照插入顺序组织 默认值false
void recordAccess(HashMap<K,V> m) {
LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
if (lm.accessOrder) {
lm.modCount++;
remove();
addBefore(lm.header);
}
}
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;
}
(LinkedHashMap实现)
public V get(Object key) {
Entry<K,V> e = (Entry<K,V>)getEntry(key);
if (e == null)
return null;
e.recordAccess(this);
return e.value;
}
LinkedHashMap的适用场景:
存储的数据是键值对,且需要保证数据按插入顺序有序的话需要使用LinkedHashMap