java 源码解析(02) LinkedHashMap
来源:互联网 发布:申威26010 知乎 编辑:程序博客网 时间:2024/05/16 13:38
一、总述
LinkedHashMap是一个有序的HashMap。HashMap源码分析请看《java 源码解析(01) HashMap》。
特点:
a、继承于HashMap,基于HashMap实现映射功能
b、增加对key的排序功能
b1、通过构造参数实现基于插入排序(key先插入的排在前面)
b2、通过构造参数实现基于访问排序(最后访问排在后面——LRU(Least Recently Used)算法)
LinkedHashMap的键值对存储结构(LinkedEntry)也是基于HashMap的键值对HashMapEntry扩展的
LinkedEntry在HashMapEntry基础上增加了两个属性,用于指向上一个和下一个的属性,这就构成了一个双向链表
注意:这个链表和HashMapEntry在HashMap里面维护的那个链表不一样。
LinkedEntry是基于Key的顺序产生的一个链表,下一个元素引用是LinkedEntry的nxt属性;
HashMapEntry是基于Key的hash地址一样的键值对构成的链表,下一个元素的引用还是HashMapEntry的next属性
也就是一堆元素(LinkedEntry也是HashMapEntry)他们之间关系存在于两种链表
LinkedEntry源码:
static class LinkedEntry<K, V>extends HashMapEntry<K, V> { LinkedEntry<K, V> nxt; LinkedEntry<K, V> prv; LinkedEntry() { // 用于创建链表头 super(null, null, 0, null); nxt = prv = this; } LinkedEntry(K key, V value, int hash,HashMapEntry<K, V> next, LinkedEntry<K, V> nxt, LinkedEntry<K,V> prv) { super(key, value, hash, next); this.nxt = nxt; this.prv = prv; } }
二、LinkedHashMap的属性含义及作用
/** 双向链表(其实是一个环形双向链表)的链表头,该指针对应的元素不存储键值对信息 **/ transient LinkedEntry<K, V> header; /** 是否基于访问排序 **/ private final boolean accessOrder;
三、LinkedHashMap的构造方法
1、带accessOrder的构造方法
其中参数initialCapacity和loadFactor用于构造父类HashMap,而参数accessOrder用于控制排序方式accessOrder 为true 基于访问排序;为false基于插入排序
public LinkedHashMap(intinitialCapacity, float loadFactor, boolean accessOrder) { super(initialCapacity, loadFactor); init(); this.accessOrder = accessOrder; }
其中init方法用于初始化链表头
源码:
void init() { header = new LinkedEntry<K, V>(); }
2、其他构造方法
这些构造都是构造出基于插入排序的LinkedHashMap,其他参数和HashMap构造方法一样
四、LinkedHashMap的操作API
1、put方法
该方法使用父类实现,这里只是重写了添加新键值对方法。步骤:
a、获取出最前面的元素,通过调用removeEldestEntry判断是否删除该元素,如果是直接删除
b、将新元素插入到链表尾
注:由于使用环形双向链表实现,所以链表头的前一个元素就是链表的最后元素,因此直接定位到最后一个位置插入
void addNewEntry(K key, V value, inthash, int index) { LinkedEntry<K, V> header =this.header; LinkedEntry<K, V> eldest =header.nxt; if (eldest != header &&removeEldestEntry(eldest)) { remove(eldest.key); } LinkedEntry<K, V> oldTail =header.prv; LinkedEntry<K, V> newTail = newLinkedEntry<K,V>(key, value, hash, table[index], header, oldTail); table[index] = oldTail.nxt = header.prv= newTail; }
其中removeEldestEntry方法默认返回false,我们可以通过重写该方法实现true
源码:
protected booleanremoveEldestEntry(Map.Entry<K, V> eldest) { return false; }
2、get方法
步骤基本也是和HashMap一样,只是查找到了而且基于访问排序的话,就会调用makeTail方法重新排序
public V get(Object key) { if (key == null) { HashMapEntry<K, V> e =entryForNullKey; if (e == null) return null; if (accessOrder) makeTail((LinkedEntry<K,V>) e); return e.value; } int hash = secondaryHash(key); HashMapEntry<K, V>[] tab = table; for (HashMapEntry<K, V> e =tab[hash & (tab.length - 1)]; e != null; e = e.next) { K eKey = e.key; if (eKey == key || (e.hash == hash&& key.equals(eKey))) { if (accessOrder) makeTail((LinkedEntry<K,V>) e); return e.value; } } return null; }
makeTail方法:
将该元素转移到链表尾
private void makeTail(LinkedEntry<K, V> e) { e.prv.nxt = e.nxt; e.nxt.prv = e.prv; LinkedEntry<K, V> header =this.header; LinkedEntry<K, V> oldTail =header.prv; e.nxt = header; e.prv = oldTail; oldTail.nxt = header.prv = e; modCount++; }
3、键值对被修改之前通知方法实现
该操作也会根据是否基于访问排序来重新排序双向链表
源码:
void preModify(HashMapEntry<K,V> e) { if (accessOrder) { makeTail((LinkedEntry<K, V>)e); } }
4、键值对配移除之后通知方法实现
从双向链表中删除该元素
源码:
void postRemove(HashMapEntry<K,V> e) { LinkedEntry<K, V> le =(LinkedEntry<K, V>) e; le.prv.nxt = le.nxt; le.nxt.prv = le.prv; le.nxt = le.prv = null; // Help the GC(for performance) }
5、获取Map内部集合数据方法
原理也和HashMap一样,最主要的不同之处就是迭代器的实现
通过双向链表顺序进行迭代
源码:
private abstract classLinkedHashIterator<T> implements Iterator<T> { LinkedEntry<K, V> next =header.nxt; LinkedEntry<K, V> lastReturned =null; int expectedModCount = modCount; public final boolean hasNext() { return next != header; } final LinkedEntry<K, V> nextEntry(){ if (modCount != expectedModCount) throw newConcurrentModificationException(); LinkedEntry<K, V> e = next; if (e == header) throw newNoSuchElementException(); next = e.nxt; return lastReturned = e; } public final void remove() { if (modCount != expectedModCount) throw newConcurrentModificationException(); if (lastReturned == null) throw newIllegalStateException(); LinkedHashMap.this.remove(lastReturned.key); lastReturned = null; expectedModCount = modCount; } }
五、总结
LinkedHashMap比HashMap多维护了一个双向环形链表
- java 源码解析(02) LinkedHashMap
- java LinkedHashMap源码解析
- Java LinkedHashMap源码解析
- java.util.LinkedHashMap源码解析
- Java集合之LinkedHashMap源码解析
- LinkedHashMap 源码解析
- LinkedHashMap源码解析
- LinkedHashMap源码解析
- LinkedHashMap源码解析
- LinkedHashMap源码解析
- LinkedHashMap源码解析
- LinkedHashMap源码解析
- LinkedHashMap类源码解析
- LinkedHashMap源码解析
- LinkedHashMap源码解析
- LinkedHashMap 源码解析
- LinkedHashMap源码解析
- 《Java源码解析》集合框架Map之LinkedHashMap
- 无人驾驶之车道线检测简易版
- 虚拟机下CentOS7开启SSH连接
- Android自定义View的实现方法,带你一步步深入了解View(四)
- Pinterest推荐系统四年进化之路--王栋
- LearnOpenGL #02 Hello Triangle
- java 源码解析(02) LinkedHashMap
- Hyper-V 怎样拷贝文件至虚拟硬盘并附加到虚拟机上
- 设计模式——桥接模式
- 回调函数
- UESTC 1006 最长上升序列 脑洞+简单dp
- waitAndnotify(简单示例)
- 转发SQLSERVER数据库索引实现
- 单链表的反转算法
- 子线程父线程交替执行(示例)