【深入学习java集合系列】LinkedHashMap的底层实现
来源:互联网 发布:网络有初心 编辑:程序博客网 时间:2024/05/16 04:43
最近写到LeetCode上的某一题LRUCache。可以采用LinkedHashMap实现,通过重写removeEldestEntry方法,即可实现。
LinkedHashMap map; public LRUCache(int capacity) { map = new LinkedHashMap<Integer, Integer>(capacity, 0.75f, true) { // 定义put后的移除规则,大于容量就删除eldest protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) { return size() > capacity; } }; } public int get(int key) { return map.containsKey(key) ? (int)map.get(key) : -1 ; } public void put(int key, int value) { map.put(key, value); }
但是,这样实在太耍赖了,所以决定研究LinkedHashMap的底层到底是如何实现的?源码应该是最好的学习代码了。
1、继承关系
- public class LinkedHashMap<K,V>
- extends HashMap<K,V>
- implements Map<K,V>
linkedhashmap继承hashMap,底层的主要存储结构是Hashmap的table。LinkedHashMap实现与HashMap的不同之处在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,该迭代顺序可以是插入顺序或者是访问顺序。
2、成员变量
- private transient Entry<K,V> header;
- private final boolean accessOrder;
首先header是一个双向链表,具体体现在Entry结构中,我们知道hashmap的Entry是一个单向链表,只有一个next属性。
其次就是accessOrder,它决定了linkedhashmap中的元素在遍历的时候的输出顺序(插入顺序或者是访问顺序)。
3、Entry对象
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); }}
4、构造函数
- //默认accessOrder为false
- //调用HashMap构造函数
- public
LinkedHashMap() { -
super(); -
accessOrder = false; - }
-
- //如果想实现LRU算法,参考这个构造函数
- public
LinkedHashMap( intinitialCapacity, floatloadFactor, -
boolean accessOrder) { -
super(initialCapacity, loadFactor); -
this.accessOrder = accessOrder; - }
-
- //模板方法模式,HashMap构造函数里面的会调用init()方法
- //初始化的时候map里没有任何Entry,让header.before
= header.after = header - void
init() { -
header = new Entry(- 1,null, null, null); -
header.before = header.after = header; - }
5、存储数据
LinkedHashMap并未重写父类HashMap的put方法,而是重写了父类HashMap的put方法调用的子方法void addEntry(int hash, K key, V value, int bucketIndex) 和void createEntry(int hash, K key, V value, int bucketIndex),提供了自己特有的双向链接列表的实现。
- //LinkedHashMap没有put(K
key, V value)方法,只重写了被put调用的addEntry方法 - //1是HashMap里原有的逻辑,23是LinkedHashMap特有的
- void
addEntry( inthash, intK key, V value, bucketIndex) { -
createEntry(hash, key, value, bucketIndex); -
-
Entry eldest = header.after; -
//3.如果有必要,移除LRU里面最老的Entry,否则判断是否该resize -
if (removeEldestEntry(eldest)) { -
removeEntryForKey(eldest.key); -
} else { -
if (size >= threshold) -
resize(2 * table.length); -
} - }
-
- void
createEntry( inthash, intK key, V value, bucketIndex) { -
//1.同HashMap一样:在Entry数组+next链表结构里面加入Entry -
HashMap.Entry old = table[bucketIndex]; -
Entry e = new Entry(hash, key, value, old); -
table[bucketIndex] = e; -
//2.把新Entry也加到header链表结构里面去 -
e.addBefore(header); -
size++; - }
-
- //默认是false,我们可以重写此方法
- protected
boolean removeEldestEntry(Map.Entry eldest) { -
return false; - }
-
- private
static class Entry extendsHashMap.Entry { -
//链表插入元素四个步骤,对着图看 -
private void addBefore(Entry existingEntry) { -
after = existingEntry; //1 -
before = existingEntry.before; //2 -
before.after = this; -
after.before = this; -
} -
} -
-
//如果走到resize,会调用这里重写的transfer - //HashMap里面的transfer是n
* m次运算,LinkedHashtable重写后是n + m次运算 - void
transfer(HashMap.Entry[] newTable) { -
int newCapacity = newTable.length; -
//直接遍历header链表,HashMap里面是遍历Entry数组 -
for (Entry e = header.after; e != header; e = e.after) { -
int index = indexFor(e.hash, newCapacity); -
e.next = newTable[index]; -
newTable[index] = e; -
} -
}
LinkedHashMap重写了父类HashMap的get方法,实际在调用父类getEntry()方法取得查找的元素后,再判断当排序模式accessOrder为true时,记录访问顺序,将最新访问的元素添加到双向链表的表头,并从原来的位置删除。由于的链表的增加、删除操作是常量级的,故并不会带来性能的损失。
- //重写了get(Object
key)方法 - public
V get(Object key) { -
//1.调用HashMap的getEntry方法得到e -
Entry e = (Entry) getEntry(key); -
if (e null)== -
return null; -
//2.LinkedHashMap牛B的地方 -
e.recordAccess(this); -
return e.value; - }
-
-
// 继承了HashMap.Entry - private
static class Entry extendsHashMap.Entry { -
//1.此方法提供了LRU的实现 -
//2.通过12两步,把最近使用的当前Entry移到header的before位置,而LinkedHashIterator遍历的方式是从header.after开始遍历,先得到最近使用的Entry -
//3.最近使用是什么意思:accessOrder为true时,get(Object key)方法会导致Entry最近使用;put(K key, V value)/putForNullKey(value)只有是覆盖操作时会导致Entry最近使用。它们都会触发recordAccess方法从而导致Entry最近使用 -
//4.总结LinkedHashMap迭代方式:accessOrder=false时,迭代出的数据按插入顺序;accessOrder=true时,迭代出的数据按LRU顺序+插入顺序 -
// HashMap迭代方式:横向数组 * 竖向next链表 -
void recordAccess(HashMap m) { -
LinkedHashMap lm = (LinkedHashMap) m; -
//如果使用LRU算法 -
if (lm.accessOrder) { -
lm.modCount++; -
//1.从header链表里面移除当前Entry -
remove(); -
//2.把当前Entry移到header的before位置 -
addBefore(lm.header); -
} -
} -
-
//让当前Entry从header链表消失 -
private void remove() { -
before.after = after; -
after.before = before; -
} -
}
7、删除数据
-
继承了HashMap.Entry - private
static class Entry extendsHashMap.Entry { -
//LinkedHashMap没有重写remove(Object key)方法,重写了被remove调用的recordRemoval方法 -
//这个方法的设计也和精髓,也是模板方法模式 -
//HahsMap remove(Object key)把数据从横向数组 * 竖向next链表里面移除之后(就已经完成工作了,所以HashMap里面recordRemoval是空的实现调用了此方法 -
//但在LinkedHashMap里面,还需要移除header链表里面Entry的after和before关系 -
void recordRemoval(HashMap m) { -
remove(); -
} -
-
//让当前Entry从header链表消失 -
private void remove() { -
before.after = after; -
after.before = before; -
} - }
- private
abstract class LinkedHashIterator implementsIterator { -
//从header.after开始遍历 -
Entry nextEntry = header.after; -
-
Entry nextEntry() { -
if (modCount != expectedModCount) -
throw new ConcurrentModificationEx ception(); -
if (nextEntry == header) -
throw new NoSuchElementException(); -
-
Entry e = lastReturned = nextEntry; -
nextEntry = e.after; -
return e; -
} - }
九.总结
- LinkedHashMap继承HashMap,结构2里数据结构的变化交给HashMap就行了。
- 结构1里数据结构的变化就由LinkedHashMap里重写的方法去实现。
- 简言之:LinkedHashMap比HashMap多维护了一个链表。
- 【深入学习java集合系列】LinkedHashMap的底层实现
- 深入Java集合学习系列:LinkedHashMap的实现原理
- 深入Java集合学习系列:LinkedHashMap的实现原理
- 深入Java集合学习系列:LinkedHashMap的实现原理
- 深入Java集合学习系列:LinkedHashMap的实现原理
- 深入Java集合学习系列:LinkedHashMap的实现原理
- 深入Java集合学习系列:LinkedHashMap的实现原理
- 深入Java集合学习系列:LinkedHashMap的实现原理
- 深入Java集合学习系列:LinkedHashMap的实现原理
- 深入Java集合学习系列:LinkedHashMap的实现原理
- 深入Java集合学习系列:LinkedHashMap的实现原理
- 深入Java集合学习系列:LinkedHashMap的实现原理
- 深入Java集合学习系列:LinkedHashMap的实现原理
- 深入Java集合学习系列:LinkedHashMap的实现原理
- 【深入理解java集合系列】LinkedHashMap实现原理
- 深入学习java集合:LinkedHashMap<K,V>实现
- 深入Java集合:LinkedHashMap实现原理
- Java集合深入学习总结-LinkedHashMap
- SAE数据库完美备份加强版
- 一个用粒子动画显示文字的 Android 自定义 View
- 阿里云将增设马来西亚数据中心_纳吉布总理大赞中国技术
- js代码段3:限制只能输入数字(兼容)
- 编码和字符串
- 【深入学习java集合系列】LinkedHashMap的底层实现
- iphone手机找回历程
- 你所不知道的js讲解
- 如何使用Bandicam录制1080P超清视频上传优酷?
- linux 查看硬件配置命令
- 如何完美的将对话框设置成无边框无标题栏样式?
- 特征工程与文本处理
- Redis监控技巧
- APP发数据到ESP8266这个WiFi模块数据发生错乱