Java集合-LinkedHashMap工作原理
来源:互联网 发布:c语言统计单词个数 编辑:程序博客网 时间:2024/06/06 14:12
概述
在了解了Java集合-HashMap源码实现深入解析后,再来看LinkedHashMap就简单的多了。
先来个示例程序:
LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();map.put("语文", "1");map.put("数学", "2");map.put("英语", "3");map.put("历史", "4");map.put("政治", "5");map.put("地理", "6");map.put("生物", "7");map.put("化学", "8");map.put("物理", "9");for(Entry<String, String> entry : map.entrySet()) { System.out.println(entry.getKey() + ": " + entry.getValue());}
想想输出的结果是什么?结果顺序怎样?
语文: 1数学: 2英语: 3历史: 4政治: 5地理: 6生物: 7化学: 8物理: 9
有没有发现,输出结果的顺序和元素插入的顺序一致?而我们再HashMap中插入的顺序和输出的顺序却不一致。Why?要解释这个问题,必须从其数据存储结构说起。
数据结构
代码调试观察的变量结果:
根据上述调试结果,画出了如下的结构图:
再看看源码中的变量声明:
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V> { /** * The head of the doubly linked list. */ private transient Entry<K,V> header; private final boolean accessOrder; // 其他省略}
源码中只声明了一个header的Entry节点和accessOrder属性:
(1)accessOrder:迭代顺序标识,默认为false,按插入顺序迭代,true则按访问顺序迭代。
(2)header节点:实现链表的关键。是一个Entry节点,该Entry继承自HashMap,采用内部类实现,源码如下:
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; // 其他省略}
因此LinkedHashMap内部的Entry节点除了继承HashMap.Entry的key、value、hash、next引用属性外,还添加了before和after引用节点以支持双向链表。这里只见到了header,没有tail,因此header其实也充当了tail的角色,正如所图中所示,所有元素和header一起形成互连的双向链表结构。到此,就拥有了链表的结构。
而LinkedHashMap还拥有HashMap一样的结构,原因在于其继承自HashMap,因此HashMap中用于存储元素的table也被继承过来,于是乎元素的存储也具备和HashMap一样的规则,所有元素存储在table中,table下的每个索引在原HashMap的结构下也是一个单向链表,同时每个元素中额外增加的before和after引用将table中的元素互连起来。正如调试代码中看到的那样。
到此我们就不难理解,为什么默认情况下,我们迭代时的顺序和插入的顺序一致了。因为迭代时,LinkedHashMap总是先从head出发,根据before引用遍历到所有元素,而在链表结构上插入元素总是插入到末尾。
put方法原理
LinkedHashMap没有直接重写HashMap的put方法,而是重写了HashMap中put方法用到的addEntry和createEntry方法,方法源码如下:只是多了一个after和before引用链的操作。
void addEntry(int hash, K key, V value, int bucketIndex) { // 先调用HashMap的addEntry方法存到table中 super.addEntry(hash, key, value, bucketIndex); // 设置before,after引用,总是放在header的after引用上 // Remove eldest entry if instructed Entry<K,V> eldest = header.after; if (removeEldestEntry(eldest)) { removeEntryForKey(eldest.key); } } 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);// 从这行代码可以看出和header建立了关系 size++; }
其中Entry的addBefore方法实现如下,就是把header的after指向新元素,原来的尾部元素也指向新元素:
private void addBefore(Entry<K,V> existingEntry) { after = existingEntry; before = existingEntry.before; before.after = this; after.before = this; }
既然LinkedHashMap继承自HashMap,那么扩容规则也和HashMap一样。
get方法原理
LinkedHashMap重新了get方法:
public V get(Object key) { Entry<K,V> e = (Entry<K,V>)getEntry(key);// getEntry用的是父类的方法 if (e == null) return null; e.recordAccess(this); return e.value; }
实现基本和HashMap一致,e.recordAccess(this)
, 这个调用是设置accessOrder为true时有效的。即按访问顺序读取,这个方法会将最近访问的元素移动到链表末尾,方便下次访问,因此如果设置了这个参数,不管插入还是读取,都会改变链表的结构,在使用时需要谨慎评估。
void recordAccess(HashMap<K,V> m) { LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m; if (lm.accessOrder) { lm.modCount++; remove(); addBefore(lm.header); } }
总结
1、LinkedHashMap继承于HashMap。
2、数据存储结构结合了Hash表和链表结构,元素存储用Hash表,元素链接通过继承父类Entry节点并增加before和after引用实现。
3、LinkedHashMap的迭代顺序收到accessOrder属性的影响,默认为false,按插入顺序访问,true按上次读取顺序访问:即每次访问元素时会把元素移动到链表末尾方便下次访问,结构会时刻变化。
白天在加班加点的干活,还要抽出时间成为技术知识的分享者,分享不易,转载请注明出处。
- Java集合-LinkedHashMap工作原理
- Java 集合:LinkedHashMap工作原理及实现
- Java 集合 3:LinkedHashMap工作原理及实现
- Java集合----LinkedHashMap的实现原理
- Core Java --集合--LinkedHashMap的实现原理
- Java集合3: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的实现原理
- JavaScript高级程序设计——读书笔记第二回
- Android apk 反编译
- 【jzoj5315】【NOIP2017提高A组模拟8.19】【小串串】【sam 】
- 编程中位运算用法总结
- 【c#机房重构】之存储过程
- Java集合-LinkedHashMap工作原理
- Centos 7.0配置LAMP(Linux+Apache HTTP+MySQL+PHP)
- squid 单进程 高并发解决方案
- 考试题目——雪后村庄
- JAVA数据结构和算法:第一章(时间复杂度和空间复杂度)
- Hadoop Operations(Hadoop操作) 详解(二) HDFS
- 字符串算法总结
- CodeForces 821E Okabe and El Psy Kongroo
- JSP常用标签——JSTL标签和EL表达式