LinkedHashMap源码分析
来源:互联网 发布:sails.js 开发api 编辑:程序博客网 时间:2024/05/17 08:50
一、声明
摘自jdk1.8
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>
二、概述
LinkedHashMap继承了HashMap,在HashMap基础上,通过维护一个双向链表来保证迭代顺序与插入顺序一致,也可以通过指定参数accessOrder=true,来保证迭代顺序为访问顺序(put、get都算访问)。
Map<String,String> map = new LinkedHashMap<String, String>();map.put("k1","1");map.put(null,"null");map.put("k2","2");map.put("k3","3");map.put("k4","4");for(Map.Entry<String, String> entry : map.entrySet()) { System.out.println(entry.getKey() + ": " + entry.getValue());}
输出结果:
k1: 1null: nullk2: 2k3: 3k4: 4
LinkedHashMap有两个属性head,tail,是双向链表的头节点和尾节点,其节点是LinkedHashMap.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); }}
LinkedHashMap覆盖了HashMap的newNode,newTreeNode方法,调用put方法时会将HashMap.Node换为LinkedHashMap.Entry(put方法详情可查看上一篇 java8-HashMap源码分析),从而构建出一条双向链表。
Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) { LinkedHashMap.Entry<K,V> p = new LinkedHashMap.Entry<K,V>(hash, key, value, e); linkNodeLast(p); return p;}// link at the end of listprivate void linkNodeLast(LinkedHashMap.Entry<K,V> p) { LinkedHashMap.Entry<K,V> last = tail; tail = p; if (last == null) head = p; else { p.before = last; last.after = p; }}
内存中的存储结构如下:
三、重要方法
HashMap中有三个空实现的方法:
// Callbacks to allow LinkedHashMap post-actions void afterNodeAccess(Node<K,V> p) { } void afterNodeInsertion(boolean evict) { } void afterNodeRemoval(Node<K,V> p) { }
LinkedHashMap对这三个方法做了具体实现,从注释和方法名字可以看出它们是 节点访问后、节点插入后、节点移除后的回调方法。
1.afterNodeAccess()
void afterNodeAccess(Node<K,V> e) { // move node to last LinkedHashMap.Entry<K,V> last; //如果是按访问顺序排序,且当前节点不是尾节点,则将当前节点移动到末尾 if (accessOrder && (last = tail) != e) { LinkedHashMap.Entry<K,V> p = (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after; p.after = null; if (b == null) head = a; else b.after = a; if (a != null) a.before = b; else last = b; if (last == null) head = p; else { p.before = last; last.after = p; } tail = p; ++modCount; } }
2.afterNodeInsertion()
void afterNodeInsertion(boolean evict) { // possibly remove eldest LinkedHashMap.Entry<K,V> first; //当自定义的溢出规则成立,则移除头节点 if (evict && (first = head) != null && removeEldestEntry(first)) { K key = first.key; removeNode(hash(key), key, null, false, true); } }
removeEldestEntry方法默认是返回false,如果自定义溢出规则,则可以将最近最少访问的节点移除,这就是一个经典的LRU置换算法的实现。
//代码摘自mysql jdbc包package com.mysql.jdbc.util;import java.util.LinkedHashMap;import java.util.Map.Entry;public class LRUCache extends LinkedHashMap { private static final long serialVersionUID = 1L; protected int maxElements; public LRUCache(int maxSize) { super(maxSize); this.maxElements = maxSize; } protected boolean removeEldestEntry(Entry eldest) { return this.size() > this.maxElements; }}
3.afterNodeRemoval()
void afterNodeRemoval(Node<K,V> e) { // unlink LinkedHashMap.Entry<K,V> p = (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after; //执行remove操作后,将链表中对应节点也删除 p.before = p.after = null; if (b == null) head = a; else b.after = a; if (a == null) tail = b; else a.before = b; }
4.put、get方法
LinkedHashMap并未覆盖put方法。get方法则重新实现并加入了afterNodeAccess来保证访问顺序。
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; }
这里要特别注意,当设置按访问顺序排序时,调用get方法也会更新链表,因此以下代码迭代会抛出ConcurrentModificationException,应当使用entrySet迭代。
for(Iterator<String> iterator = map.keySet().iterator(); iterator.hasNext();){ String name = iterator.next(); System.out.println(name+":"+map.get(name));}结果:k1:1Exception in thread "main" java.util.ConcurrentModificationException at java.util.LinkedHashMap$LinkedHashIterator.nextNode(LinkedHashMap.java:719) at java.util.LinkedHashMap$LinkedKeyIterator.next(LinkedHashMap.java:742) at hub.UserVo.main(UserVo.java:32)
四、总结
- LinkedHashMap继承HashMap,可以按插入顺序排序,也可以通过设置参数accessOrder=true,按访问顺序排序,其内部通过维护一个双向链表来保证迭代顺序;
- 覆盖了afterNodeAccess、afterNodeAccess、afterNodeRemoval三个方法;
- 通过指定accessOrder=true,并覆盖removeEldestEntry方法自定义溢出规则可以实现LRUCache。
参考资料:
Java LinkedHashMap工作原理及实现
- HashMap LinkedHashMap源码分析
- LinkedHashMap源码分析
- LinkedHashMap源码分析
- LinkedHashMap源码分析
- LinkedHashMap源码分析
- LinkedHashMap源码分析
- 《Java源码分析》:LinkedHashMap
- LinkedHashMap及其源码分析
- LinkedHashMap源码分析
- LinkedHashMap及其源码分析
- LinkedHashMap源码分析
- LinkedHashMap源码分析
- LinkedHashMap源码分析
- LinkedHashMap及其源码分析
- 《Java源码分析》:LinkedHashMap
- LinkedHashMap源码分析
- LinkedHashMap源码分析
- LinkedHashMap的源码分析
- 精通脚本黑客 2.2 'or'='or'经典漏洞攻击
- ARM推出最新调试器DSTREAM-ST
- Climbing Stairs II
- 【JavaScript】var that = this
- 【chrome】阻止浏览器表单填充
- LinkedHashMap源码分析
- 提取字符串中的数字
- HDU 2007 平方和与立方和
- 利用jna调用使用C#的dll文件
- (JNI)c代码利用反射调用java方法
- VTK:读取obj文件,使用vtkMassProperties计算obj三维模型的体积和面积
- [实践]房价预测
- 实现反向传播算法的细节
- 傻瓜式配置gulp