LinkedHashMap代码详解(一)
HashMap 在遍历map时,数据是无序的,在某些需要按照put顺序遍历时,就用到了LinkedHashMap,LinkedHashMap是HashMap的子类,并且用一条双向链表来存储数据插入的顺序
transient LinkedHashMap.Entry<K,V> head; //链表的头节点 transient LinkedHashMap.Entry<K,V> tail; //链表的尾节点 final boolean accessOrder; //是否开启lru算法(最活跃的点放在链表头部)
put():
由于LinkedHashMap没有put方法,put操作用的还是HashMap的方法,但是重写了newNode(int hash, K key, V value, Node e) ,afterNodeAccess(Node e),afterNodeInsertion(boolean evict)方法
1 table中不存在相同的key
if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null);
LinkedhashMap重写了newNode方法,将新增的节点加入链表尾部,上代码
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; } private void linkNodeLast(LinkedHashMap.Entry<K,V> p) { LinkedHashMap.Entry<K,V> last = tail; tail = p;//尾节点指向新节点 if (last == null)//如果原尾节点为空,证明链表还没有数据,头节点指向新节点p head = p; else { //链表不为空 p.before = last; last.after = p;//原尾节点建立双向链接 } }
2 table数组中存在相同的key
if (e != null) { // existing mapping for key V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; }
发生了节点数据操作(value替换),如果开启了LRU(accessOrder=true),则将修改的节点放入链表尾部,HashMap.afterNodeAccess() 为空函数
LinkedHashMap overwirte了该方法
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; /**重写上面的赋值操作 LinkedHashMap.Entry<K,V> p =(LinkedHashMap.Entry<K,V>)e; LinkedHashMap.Entry<K,V> b(befor) = p.before; LinkedHashMap.Entry<K,V> a(after) = p.after; **/ p.after = null; //将修改的节点after置为null if (b == null) //如果b==null 证明e是头结点 head = a; //将链表头结点指向 e.next else //e不是头结点 b.after = a; // e.befor.after = e.after // e节点的上一个节点的next指向e的下一个节点,移除e if (a != null) //如果e的下一个节点不为空 a.before = b;//e.after.befor= e.after // e节点的下一个节点的befor指向e的上一个节点,双向链表的反向箭头 else // a == null e是尾部节点 last = b; //last指向e的上一个节点 if (last == null) //如果 last== null 证明 只有一个节点 head = p; //头部尾部都指向该节点 else { // p不是头节点 p.before = last; //将p放在链表尾部 last.after = p; } tail = p; ++modCount; //map操作数+1,(重新赋值且开启LRU,modCount会两次+1) } }
3 在执行完put操作后,调用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); } } protected boolean removeEldestEntry(Map.Entry<K,V> eldest) { return false; }
evict 在hashMap中默认为true,LinkedHashMap.removeEldestEntry ()默认返回false,
该方法的yongfasj用法是 配合LRU算法,自定义Class继承LinkedHashMap方法并重写removeEldestEntry 方法,
在特定条件下返回true,移除链表中最左侧的节点
可以使用该特性进行缓存创建,保留最近活跃的数据,开启LRU需要将开头提到的accessOrder 置为true
该参数只能在LinkedHashMap new时指定,需要同时指定扩展因子loadFactor 默认为0.75f
public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) { super(initialCapacity, loadFactor); this.accessOrder = accessOrder; }
get():
LinkedHashMap get方法,可以看到 和hashMap是一致的,只是加了accessOrder 判断,开启LRU后,将获取的数据放在链表的最右侧,提高该节点的活跃程度
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; }
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Spring还可以这样用缓存,你知道吗?
大家在项目开发过程中,或多或少都用过缓存,为了减少数据库的压力,把数据放在缓存当中,当访问的请求过来时,直接从缓存读取。缓存一般都是基于内存的,读取速度比较快,市面上比较常见的缓存有:memcache、redis、mongodb、guava cache等。 缓存的常规用法 大家使用缓存时,常用的逻辑时这样的: 根据条件生成key; 从缓存中读取数据,若成功读取数据,则返回; 若数据不存在,根据条件从数据库读取; 将从数据库中读取的数据放入缓存; 返回数据; 每一个使用缓存的场景,上面的逻辑都要重写一遍,是不是很烦躁,是不是很浪费时间。有没有简单的方法完成上面的逻辑?当然有了,这就是今天要向大家介绍的Spring Cache。 Spring Cache Spring Cache并不神秘,而且使用起来非常的方便。它是注解组成的,最常用的一个注解是@Cacheable。这个注解是在方法上使用的,当使用了注解的方法被调用时,会先从缓存中查询,如果缓存中不存在,则执行方法,然后将方法的返回值放入缓存中。具体的使用方法如下: 首先,我们在IDEA中使用Spring Boot搭建环境,在选择依赖的页...
- 下一篇
Giraph源码分析(四)—— Master 如何检查Worker启动成功
本文的目的 说明Giraph如何借助ZooKeeper来实现Master与Workers间的同步(不太确定)。 环境 在单机上(机器名:giraphx)启动了2个workers。 Giraph遵从单Master多Workers结构,BSPServiceMaster使用MasterThread线程来进行全局的同步。每个Worker启动成功后,会向Master汇报自身的健康状况,那么Master是如何检测Workers是否都成功启动了? Master在ZooKeeper上创两个目录,_workerHealthyDir和 _workerUnhealthyDir,分别用来记录Healthy Workers和UnHealthy Workers。 主要在BspServiceMaster类中的getAllWorkerInfos()方法来完成,其调用关系如下,注意下getAllWorkerInfos()到MasterThread.run()方法调用关系,比较难找。 创建的两个目录如下: /_hadoopBsp/job_201404102333_0002/_applicationAttemptsDir/...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- SpringBoot2整合Redis,开启缓存,提高访问速度
- CentOS8编译安装MySQL8.0.19
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS7,8上快速安装Gitea,搭建Git服务器
- Docker安装Oracle12C,快速搭建Oracle学习环境
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- Windows10,CentOS7,CentOS8安装Nodejs环境