LinkedHashMap

LinkedHashMap内部採用了散列表和链表实现Map接口,并能够保证迭代的顺序,和HashMap不同,其内部维护一个指向全部元素的双向链表,其决定了遍历的顺序,一般是元素插入的顺序进行迭代,只是元素又一次插入顺序不会受到影响。

LinkedHashMap提供一个特殊的构造函数,实现了每次迭代返回近期使用的元素,这个特性能够用于构建LRU缓存。

此外removeEldestEntry(Map.Entry)方法能够被子类覆盖用于推断在加入元素的时候什么时候能够删除元素。

LinkedHashMap性能相同受到初始容量和装填因子的影响,对于基本操作(add,contains,remove)在常数时间内,其性能比HashMap略微低。因为须要额外代价维护链表;只是其遍历性能为O(size)高于HashMapO(capacity)。

LinkedHashMap实现

类定义

直接继承了HashMap

public class LinkedHashMap<K,V>
extends HashMap<K,V>
implements Map<K,V>

成员

private transient Entry<K,V> header; //用于遍历的双向链表表头

/**
* The iteration ordering method for this linked hash map:
* true: for access-order, false: for insertion-order
*/
private final boolean accessOrder;

Entry内部类继承了HashMap.Entry<K,V>类,添加了两个指针before和after用于维护遍历顺序,实际上Entry有三个指针父类本身有个next指针用于当发生元素冲突时指向的下一个元素。

由此能够看出用于遍历的双向链表直接加在Entry上面,这样有效节约了空间,实际仅仅比HashMap多了2*size个引用+1个头结点空间消耗。

before和after这两个引用在外部类调用put或remove时,调用其相关方法进行维护(recordAccess和recordRemoval等)。

 private static class Entry<K,V> extends HashMap.Entry<K,V> {
Entry<K,V> before, after; Entry(int hash, K key, V value, HashMap.Entry<K,V> next) {
super(hash, key, value, next);
} private void remove() {
before.after = after;
after.before = before;
}
//existingEntry之前加入当前节点
private void addBefore(Entry<K,V> existingEntry) {
after = existingEntry;
before = existingEntry.before;
before.after = this;
after.before = this;
}
//由父类HashMap的put方法调用,若是acessOrder。则加入到双向链表的头结点后面;本身get方法也会触发调用
void recordAccess(HashMap<K,V> m) {
LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
if (lm.accessOrder) {
lm.modCount++;
remove();
addBefore(lm.header);
}
}
//有元素删除时,调用该方法
void recordRemoval(HashMap<K,V> m) {
remove();
}
}

put方法是继承自父类HashMap,重写了当须要加入元素时候调用的addEntry方法,相同是在相应桶的链表头结点后面加入。加入完以后不是直接进行resize推断,而是推断是否要删除旧的元素,这种方法默认返回false,用户能够重写这种方法用于确定缓存的淘汰机制。

void addEntry(int hash, K key, V value, int bucketIndex) {
createEntry(hash, key, value, bucketIndex); // Remove eldest entry if instructed, else grow capacity if appropriate
Entry<K,V> eldest = header.after;
if (removeEldestEntry(eldest)) {
removeEntryForKey(eldest.key);
} else {
if (size >= threshold)
resize(2 * table.length);
}
} void createEntry(int hash, K key, V value, int bucketIndex) {
HashMap.Entry<K,V> old = table[bucketIndex];
Entry<K,V> e = new Entry<K,V>(hash, key, value, old);
table[bucketIndex] = e;
e.addBefore(header); //每次都是在相应桶链表的開始处加入
size++;
} protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
return false;
}

get方法

public V get(Object key) {
Entry<K,V> e = (Entry<K,V>)getEntry(key);
if (e == null)
return null;
e.recordAccess(this); //实现LRU
return e.value;
}
//重写父类方法,效率更高O(size)
public boolean containsValue(Object value) {
// Overridden to take advantage of faster iterator
if (value==null) {
for (Entry e = header.after; e != header; e = e.after)
if (e.value==null)
return true;
} else {
for (Entry e = header.after; e != header; e = e.after)
if (value.equals(e.value))
return true;
}
return false;
}

视图和迭代器

LinkedHashMap相同继承了创建3个集合类视图:键集合、值集合、键值对集合的方法。因为额外维护一个双向链表保证迭代顺序,重写了相关视图的迭代器实现,LinkedHashIterator通过直接迭代链表的header指针来实现指定顺序遍历。

Iterator<K> newKeyIterator()   { return new KeyIterator();   }
Iterator<V> newValueIterator() { return new ValueIterator(); }
Iterator<Map.Entry<K,V>> newEntryIterator() { return new EntryIterator(); } private class KeyIterator extends LinkedHashIterator<K> {
public K next() { return nextEntry().getKey(); }
} private class ValueIterator extends LinkedHashIterator<V> {
public V next() { return nextEntry().value; }
} private class EntryIterator extends LinkedHashIterator<Map.Entry<K,V>> {
public Map.Entry<K,V> next() { return nextEntry(); }
} private abstract class LinkedHashIterator<T> implements Iterator<T> {
Entry<K,V> nextEntry = header.after;
Entry<K,V> lastReturned = null;
int expectedModCount = modCount; public boolean hasNext() {
return nextEntry != header;
} public void remove() {
if (lastReturned == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException(); LinkedHashMap.this.remove(lastReturned.key);
lastReturned = null;
expectedModCount = modCount;
} Entry<K,V> nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
if (nextEntry == header)
throw new NoSuchElementException(); Entry<K,V> e = lastReturned = nextEntry;
nextEntry = e.after;
return e;
}
}

总结

  1. LinkedHashMap继承自HashMap。相关基本操作性能略低于HashMap。因为须要额外代价维护链表。其遍历操作是通过操作该双向链表实现,而非内部散列表数组。因此性能为O(size)比HashMapO(capacity)更高。
  2. 支持两种顺序遍历:元素插入顺序(反复put不算)和近期使用优先顺序(调用put和get类似LRU),默认是依照元素插入顺序遍历。通过构造函数传入true能够实现近期使用优先遍历。每次put或get操作时,将该元素直接又一次放置到链表头结点后面来实现近期使用优先遍历。

  3. LinkedHashMap并没有又一次创建一个新的链表来实现顺序遍历,而是在每一个Entry上多加了两个指针来决定遍历顺序。有效节约了空间消耗。

    实际仅仅比HashMap多了2*size个引用+1个头结点空间消耗。

  4. LinkedHashMap支持元素淘汰策略,能够通过重写removeEldestEntry方法。来决定调用put时候是否须要删除旧的元素。LinkedHashMap能够用于实现LRU缓存,并自己定义元素淘汰策略。

LinkedHashMap源代码阅读的更多相关文章

  1. Mongodb源代码阅读笔记:Journal机制

    Mongodb源代码阅读笔记:Journal机制 Mongodb源代码阅读笔记:Journal机制 涉及的文件 一些说明 PREPLOGBUFFER WRITETOJOURNAL WRITETODAT ...

  2. 【转】Tomcat总体结构(Tomcat源代码阅读系列之二)

    本文是Tomcat源代码阅读系列的第二篇文章,我们在本系列的第一篇文章:在IntelliJ IDEA 和 Eclipse运行tomcat 7源代码一文中介绍了如何在intelliJ IDEA 和 Ec ...

  3. 利用doxygen提高源代码阅读效率

    阅读开源项目的源代码是提高自己编程能力的好方法,而有一个好的源代码阅读工具无疑能够让你在阅读源代码时事半功倍.之前找过不少源代码阅读工具,像SourceInsight.sourcenav.scitoo ...

  4. CI框架源代码阅读笔记5 基准測试 BenchMark.php

    上一篇博客(CI框架源代码阅读笔记4 引导文件CodeIgniter.php)中.我们已经看到:CI中核心流程的核心功能都是由不同的组件来完毕的.这些组件类似于一个一个单独的模块,不同的模块完毕不同的 ...

  5. 淘宝数据库OceanBase SQL编译器部分 源代码阅读--Schema模式

    淘宝数据库OceanBase SQL编译器部分 源代码阅读--Schema模式 什么是Database,什么是Schema,什么是Table,什么是列,什么是行,什么是User?我们能够能够把Data ...

  6. CI框架源代码阅读笔记3 全局函数Common.php

    从本篇開始.将深入CI框架的内部.一步步去探索这个框架的实现.结构和设计. Common.php文件定义了一系列的全局函数(一般来说.全局函数具有最高的载入优先权.因此大多数的框架中BootStrap ...

  7. [C++ 2011 STL (VS2012 Update4) 源代码阅读系列(2)]熟悉一些宏定义和模版偏特化或叫模版专门化

    [C++ 2011 STL (VS2012 Update4) 源代码阅读系列(2)]熟悉一些宏定义和模版偏特化或叫模版专门化 // point_test.cpp : 知识点练习和测试,用于单步调试,跟 ...

  8. 【转】Tomcat源代码阅读系列

    在IntelliJ IDEA 和 Eclipse运行tomcat 7源代码(Tomcat源代码阅读系列之一) Tomcat总体结构(Tomcat源代码阅读系列之二) Tomcat启动过程(Tomcat ...

  9. 【Java集合源代码剖析】LinkedHashmap源代码剖析

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/37867985 前言:有网友建议分析下LinkedHashMap的源代码.于是花了一晚上时间 ...

随机推荐

  1. Ambari是啥?主要是干啥的?

    简单来说,Ambari是一个拥有集群自动化安装.中心化管理.集群监控.报警功能的一个工具(软件),使得安装集群从几天的时间缩短在几个小时内,运维人员从数十人降低到几人以内,极大的提高集群管理的效率. ...

  2. (转)Spring AOP的底层实现技术

    AOP概述 软件的编程语言最终的目的就是用更自然更灵活的方式模拟世界,从原始机器语言到过程语言再到面向对象的语言,我们看到编程语言在一步步用更自然.更强大的方式描述软件.AOP是软件开发思想的一个飞跃 ...

  3. 清瘦的记录者: 一个比dbutils更小巧、好用的的持久化工具

    https://gitee.com/bitprince/memory 1. 概述 1.1 连接.语句和结果集 从JDBC的规范上看,其对数据访问层有相当简洁的抽象:1.连接(connection) 2 ...

  4. 【译】x86程序员手册00 - 翻译起因

    从上一次学习MIT的操作系统课程又过去了一年.上次学习并没有坚持下去.想来虽有种种原因,其还在自身无法坚持罢了.故此次再鼓起勇气重新学习,发现课程都已由2014改版为2016了.但大部分内容并没有改变 ...

  5. Git与SVN版本控制系统

    关于版本控制 什么是版本控制?版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统.在本书所展示的例子中,我们仅对保存着软件源代码的文本文件作版本控制管理,但实际上,你可以对任 ...

  6. pptp服务故障

    pptp服务故障记录 原文地址:http://www.cnblogs.com/caoguo/p/4994512.html 1.pptp部署是遇到开了防火墙端口不能拨号,拨号是提示错误如下: 但是关闭防 ...

  7. XML在线转化为JSON

    http://www.utilities-online.info/xmltojson/

  8. Java中的方法重写

    方法的重载: 在同一个类中,出现多个同名的方法,参数列表不同,与返回值类型,修饰符无关 方法的重写: 子类中出现和父类中一模一样的方法(包括返回值类型,方法名,参数列表) 方法重写的注意事项: 1.重 ...

  9. 【源码阅读】opencv中opencl版本的dft函数的实现细节

    1.函数声明 opencv-3.4.3\modules\core\include\opencv2\core.hpp:2157 CV_EXPORTS_W void dft(InputArray src, ...

  10. 前端自动化构建工具gulp使用

    1. 全局安装 gulp: $ npm install --global gulp 2. 作为项目的开发依赖(devDependencies)安装: $ npm install --save-dev ...