包路径:package java.util;
import java.io.*;
一、继承关系
public class LinkedHashMap<K,V>
extends HashMap<K,V>
implements Map<K,V>{
}
继承于HashMap,那么HashMap中所有的非private方法都被继承下来;实现了Map接口。
二、基本属性
1、private static final long serialVersionUID = 3801124242820219131L;
2、private transient Entry<K,V> header; //链表的头节点
3、private 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;
}
构造方法四、通过传入的Map创建一个LinkedHashMap
public LinkedHashMap(Map<? extends K, ? extends V> m) {
super(m);
accessOrder = false;
}
构造方法五、传入三个参数:初始容量大小、加载因子、键值对的保持顺序创建一个LinkedHashMap
public LinkedHashMap(int initialCapacity,
float loadFactor,
boolean accessOrder) {
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder;
}
从构造方法可以知道:默认采用插入顺序来保持键值对的顺序,所有的构造方法都会调用父类的构造方法。
四、核心方法
1、添加元素:put()------>调用父类的添加元素方法
public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
if (key == null) 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); 使用自己(LinkedHashMap)实现的方法
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i); 使用自己(LinkedHashMap)实现的方法
return null;
}
子类重写的方法:
void recordAccess(HashMap<K,V> m) {
LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m; 将传进去的HashMap强转
if (lm.accessOrder) { 默认为false,但是当为true的时候,即按照访问顺序
lm.modCount++;
remove(); 移除当前节点
addBefore(lm.header); 将当前节点插入到头结点之前的位置
}
}
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); 首先调用父类的方法
Entry<K,V> eldest = header.after;
if (removeEldestEntry(eldest)) {
removeEntryForKey(eldest.key);
}
}
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
return false;
}
图例演示:这里参考了网上一位博主的博客:https://blog.csdn.net/ShelleyLittlehero/article/details/82954474
2、获取元素:get()
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;
}
3、删除元素:remove()
private void remove() { 移除节点,修改前后引用
before.after = after;
after.before = before;
}
AccessOrder作用于那些方法?
->put()[在节点重复是起作用]
->get()若为false,不改变双向链表结构,若为true:将当前节点从header组织的双向链表删除、放在header的before位置
五、LinkedHashMap的特点总结
1、底层数据结构是双向循环链表+数组
private static class Entry<K,V> extends HashMap.Entry<K,V> {
Entry<K,V> before, after;
Entry(int hash, K key, V value, HashMap.Entry<K,V> next) {
super(hash, key, value, next);
}
Entry的基本属性:K key、V value、Entry<K,V> next、int hash---->继承自HashMap.Entry<K,V>
Entry<K,V> before、 Entry<K,V> after------->LinkedHashMap特有的属性
注意:next是用来维护HashMap指定数组位置上连接的Entry的顺序;before、after是用来维护Entry插入的先后顺序的。
2、保证数据的插入有序和访问有序。有序性是通过属性accessOrder(布尔类型)来标识的,默认是插入有序(accessOrder=false),访问有序是(accessOrder=true)。
3、entry类型的header,内部没有存储数据。
4、继承于HashMap,也是非线程安全的,具有HashMap的特点,在这里不再叙述。
5、适用场景:当需要保证数据是有序的时候,可以使用该类