LinkedHashMap源码解读

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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值