1、特点:
(1)、键有序:可选插入有序或者访问有序 《《《《《主要特点:默认插入有序
(2)、以键值对<k,v>的形式储存
(3)、键不能重复,如果重复,新的值会覆盖旧的值;
(4)、键可以为有一个为null,值可以多个为null;
特定:键为null的元素放在数组0号位置;
(5)、底层数组的容量为2的指数级
2、数据结构:数组+链表
底层虽然使用的仍然是数组+链表。可是LinkedHashMap底层也使用了双向链表来使元素保持有序,当然,只是键的有序;
3、底层源码分析:
(1)继承关系:
继承 HashMap<K,V>类; 实现 Map<K,V>接口
(2)构造函数:(5个)
注意点1:LinkedHashMap的五个构造方法都调用了其父类HashMap的构造方法;
注意点2:可以传入boolean值来选择使数据保持插入顺序或者访问顺序
(3)默认值 :
默认初始容量:16,
默认初始负载因子:0.75 ,
默认排序模式:false(插入有序)
(4)增长方式:
扩容临界: 当前size >= 扩容阈值(当前容量 * 负载因子)
resize( ) : 2倍扩容
(5)基本属性:
继承HashMap,,具有hashMap的所有属性;
新增属性:
Entry<K,V> header ; 头结点,实际上此节点在链表的尾部;
此头结点不储存数据,只做头结点用,调用LinkedHashMap构造方法的时候就已经使用init方法初始化;
void init() {
header = new Entry<K,V>(-1, null, null, null);
header.before = header.after = header;
}
boolean accessOrder ; 排序模式
true 遍历时使用访问顺序
false 遍历时使用插入顺序
内部类Entry{ }中也多了两个属性:
Entry after; 后一个节点
Entry before;前一个节点
(6)增添查改
A、添加元素**************************************
put方法:LinkefHashMap并没有自己实现put方法,而是直接使用的父类HashMap的put方法,其过程分析如下:
直接调用父类的put方法添加元素:父类方法 put
put方法中调用重写的addEntry方法或者recordAccess方法;本类方法 addEntry,recordAccess
addEntry方法会调用父类的addEntry方法:为了数组扩容和重新哈希
父类addEntry会调用当前子类的createEntry方法:添加元素
然后调用addBefore方法 :使新元素保存上一个元素的索引,并将新元素的索引保存在header中;
(父类HashMap实现)
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++;
//此处调用的是子类LinkedHashMap的重写方法
addEntry(hash, key, value, i);
return null;
}
(子类LinkedHashMap实现)
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;
//此处的removeELDESTentry方法是LinkedHashMap的方法
if (removeEldestEntry(eldest)) {
removeEntryForKey(eldest.key);
}
}
(父类HashMap实现)
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);
}
(子类LinkedHashMap实现)
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;
//此处addBrfore方法的入参永远为header,因为header中保存上一个节点的索引
e.addBefore(header);
size++;
}
LinkedHashMap的核心思想:为了是元素有序,便定义了一个header节点由于记录每
个节点的索引,然后依次将head节点中保存的节点信息赋值到新加入的节点的 after
属性和before属性上,实现了键有序;
//实际上就是把当前元素插到尾节点的前面
(当前类LinkedHashMap实现)
private void addBefore(Entry<K,V> existingEntry) {
after = existingEntry;
before = existingEntry.before;
before.after = this;
after.before = this;
}
4、与hashMap不同点:
LinkedHashMap其中的内部类Entry有多出了Entry<K,V> before, after,而此举就是为了实现数据的有序性,为了根据插入顺序将节点连接起来(双向链表)
5、存在的必要性:
可以在遍历时有序;