LinkedHashMap源码分析
来源:互联网 发布:淘宝评价后不计分 编辑:程序博客网 时间:2024/05/19 20:56
LinkedHashMap继承了HashMap,在hashmap的哈希表的基础上维护了一个双向循环链表。是哈希表和链表两种数据结构的结合体。
LinkedHashMap添加了两个重要的属性:LinkedHashMap.Entry类型的header–双向链表的头;boolean类型的accessOrder,用于确定链表是以访问顺序排序还是以插入顺序排序。
LinkedHashMap中的内部类Entry类,继承自HashMap.Entry类,添加了指向链表中前后元素的两个引用。
构造器
LinkedHashMap使用父类构造器进行构造,然后添加了对accessOrder的初始化和对header的初始化工作。
LinkedHashMap在HashMap的操作的基础上添加了链表中元素的引用关系的维护操作,以及按需进行元素顺序的维护操作。
基础设施
LinkedHashMap中关于链表部分的操作都是围绕其内部类Entry来展开的。用remove()和addBefore()维护添加或删除链表元素时元素之间的引用关系;如果需要按访问顺序排列链表,则用recordAccess()和recordRemoval()来动态维护链表的元素顺序。
/** * LinkedHashMap entry. */ private static class Entry<K,V> extends HashMap.Entry<K,V> { // These fields comprise the doubly linked list used for iteration. Entry<K,V> before, after; Entry(int hash, K key, V value, HashMap.Entry<K,V> next) { super(hash, key, value, next); } /** * Removes this entry from the linked list. */ private void remove() { before.after = after; after.before = before; } /** * Inserts this entry before the specified existing entry in the list. */ private void addBefore(Entry<K,V> existingEntry) { after = existingEntry; before = existingEntry.before; before.after = this; after.before = this; } /** * This method is invoked by the superclass whenever the value * of a pre-existing entry is read by Map.get or modified by Map.set. * If the enclosing Map is access-ordered, it moves the entry * to the end of the list; otherwise, it does nothing. */ void recordAccess(HashMap<K,V> m) { LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m; //如果LinkedHashMap定义为访问顺序的链表,那么修改modCount,将该元素移至链表的尾端。如果是插入顺序,则什么也不做(不进行顺序的调整) if (lm.accessOrder) { lm.modCount++; remove(); addBefore(lm.header); } } void recordRemoval(HashMap<K,V> m) { remove(); } }
重写的方法
addEntry():
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); } }
createEntry():
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; //维护链表的前后引用关系,将新增的Entry添加至链表尾部 e.addBefore(header); size++; }
对于父类的put方法,1、如果哈希表中有相同的key,那么会调用recordAccess方法;2、如果哈希表中没有,新增Entry的话,会调用重写过的createEntry()方法,在添加新元素的同时,维护链表元素引用关系。注意:createEntry()方法是同时满足访问顺序和插入顺序的。所以不需要额外的调用recordAccess方法。
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; }
containsValue和transfer
因为有所有元素的链表,所以元素的遍历相比父类而言需要访问的地址少。(hashmap需要访问整个capacity,而linkedhashmap只需要访问size。)
总结
LinkedHashMap通过继承HashMap和重写其部分方法,并增加了Entry的属性,给哈希表里的元素附上了链表数据结构,赋予了hashmap链表的功能。
通过accessOrder属性值true,和新的Entry的recordAccess方法,加上对父类createEntry方法的重写,使linkedhashmap拥有了维护访问顺序的能力。其关键就是在每次访问(put(包括新增元素)/get)某个元素时,调整该元素的位置至链表尾端。所以该链表具有访问顺序排序。
如果accessOrder为false(默认值),则每次访问元素(包括新增元素)只是添加元素至链表尾端,不做其他操作,即为插入顺序。
访问顺序的一个典型应用就是LRU的实现。
另外通过对removeEldestEntry方法提供实现,可以将linkedhashmap作为缓存来实现。
private static final int MAX_ENTRIES = 100;protected boolean removeEldestEntry(Map.Entry eldest) { return size() > MAX_ENTRIES; }
当调用addEntry()方法时,如果size超限,那么最老的entry将被移除。
- HashMap LinkedHashMap源码分析
- LinkedHashMap源码分析
- LinkedHashMap源码分析
- LinkedHashMap源码分析
- LinkedHashMap源码分析
- LinkedHashMap源码分析
- 《Java源码分析》:LinkedHashMap
- LinkedHashMap及其源码分析
- LinkedHashMap源码分析
- LinkedHashMap及其源码分析
- LinkedHashMap源码分析
- LinkedHashMap源码分析
- LinkedHashMap源码分析
- LinkedHashMap及其源码分析
- 《Java源码分析》:LinkedHashMap
- LinkedHashMap源码分析
- LinkedHashMap源码分析
- LinkedHashMap的源码分析
- 免费开源的电路图和PCB绘图软件KiCAD
- Android开发技巧——写一个StepView
- javamail实现QQ发送邮件
- 关于.net mvc中@Html.DropDownListFor和@Html.DropDownList默认值无法选中问题
- 学习JavaScript的理由
- LinkedHashMap源码分析
- Android dumpsys用法
- 图片上传转base64预览demo及个人解析
- 继承AppCompatActivity实现全屏显示
- 发送有序广播
- 调用金蝶EAS系统标准凭证接口,封装凭证对象,调用业务接口前需先登录
- 我的路
- java 多线程的一些基础
- DWZ 树形导航菜单