LinkedHashMap相关信息介绍(转)

Java中的LinkedHashMap
此实现与 HashMap 的不同之处在于,后者维护着一个运行于所有条目的双重链接列表。
此链接列表定义了迭代顺序,该迭代顺序通常就是将键插入到映射中的顺序(插入顺序)。

LinkedHashMap和TreeMap的区别 
首先2个都是map,所以用key取值肯定是没区别的,区别在于用Iterator遍历的时候 
LinkedHashMap保存了记录的插入顺序,先插入的先遍历到 
TreeMap默认是按升序排,也可以指定排序的比较器。遍历的时候按升序遍历。 
http://blog.csdn.net/scelong/article/details/7187142

一.LinkedHashMap的存储结构

  1. LinkedHashMap是继承HashMap,也就继承了HashMap的结构,也就是图中的结构2,在下文中我用"Entry数组+next链表"来描述。而LinkedHashMap有其自己的变量header,也就是图中的结构1,下文中我用"header链表"来描述。
  2. 结构1中的Entry和结构2中的Entry本是同一个,结构1中应该就只有一个header,它指向的是结构2中的e1 e2,但这样会使结构图难画。为了说明问题的方便,我把结构2里的e1 e2在结构1中多画一个。

二.LinkedHashMap成员变量

Java代码  

  1. // LinkedHashMap维护了一个链表,header是链表头。此链表不同于HashMap里面的那个next链表
  2. private transient Entry<K, V> header;
  3. // LRU:Least Recently Used最近最少使用算法
  4. // accessOrder决定是否使用此算法,accessOrder=true使用
  5. private final boolean accessOrder;

三.LinkedHashMap里的Entry对象

Java代码  

  1. // 继承了HashMap.Entry,其他几个方法边用边分析
  2. rivate static class Entry<K, V> extends HashMap.Entry<K, V> {
  3. // 增加了两个属性,每个Entry有before Entry和after Entry,就构成了一个链表
  4. Entry<K, V> before, after;
  5. Entry(int hash, K key, V value, HashMap.Entry<K, V> next) {
  6. super(hash, key, value, next);
  7. }
  8. private void addBefore(Entry<K, V> existingEntry) {
  9. .....
  10. }
  11. void recordAccess(HashMap<K, V> m) {
  12. .....
  13. }
  14. void recordRemoval(HashMap<K, V> m) {
  15. .....
  16. }
  17. private void remove() {
  18. .....
  19. }

四.构造函数

Java代码  

  1. //默认accessOrder为false
  2. //调用HashMap构造函数
  3. public LinkedHashMap() {
  4. super();
  5. accessOrder = false;
  6. }
  7. //如果想实现LRU算法,参考这个构造函数
  8. public LinkedHashMap(int initialCapacity, float loadFactor,
  9. boolean accessOrder) {
  10. super(initialCapacity, loadFactor);
  11. this.accessOrder = accessOrder;
  12. }
  13. //模板方法模式,HashMap构造函数里面的会调用init()方法
  14. //初始化的时候map里没有任何Entry,让header.before = header.after = header
  15. void init() {
  16. header = new Entry<K, V>(-1, null, null, null);
  17. header.before = header.after = header;
  18. }

五.存数据

Java代码  

  1. //LinkedHashMap没有put(K key, V value)方法,只重写了被put调用的addEntry方法
  2. //1是HashMap里原有的逻辑,23是LinkedHashMap特有的
  3. void addEntry(int hash, K key, V value, int bucketIndex) {
  4. createEntry(hash, key, value, bucketIndex);
  5. Entry<K, V> eldest = header.after;
  6. //3.如果有必要,移除LRU里面最老的Entry,否则判断是否该resize
  7. if (removeEldestEntry(eldest)) {
  8. removeEntryForKey(eldest.key);
  9. } else {
  10. if (size >= threshold)
  11. resize(2 * table.length);
  12. }
  13. }
  14. void createEntry(int hash, K key, V value, int bucketIndex) {
  15. //1.同HashMap一样:在Entry数组+next链表结构里面加入Entry
  16. HashMap.Entry<K, V> old = table[bucketIndex];
  17. Entry<K, V> e = new Entry<K, V>(hash, key, value, old);
  18. table[bucketIndex] = e;
  19. //2.把新Entry也加到header链表结构里面去
  20. e.addBefore(header);
  21. size++;
  22. }
  23. //默认是false,我们可以重写此方法
  24. protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
  25. return false;
  26. }
  27. private static class Entry<K, V> extends HashMap.Entry<K, V> {
  28. //链表插入元素四个步骤,对着图看
  29. private void addBefore(Entry<K, V> existingEntry) {
  30. after = existingEntry;                //1
  31. before = existingEntry.before;     //2
  32. before.after = this;                   //3
  33. after.before = this;                   //4
  34. }
  35. }
  36. //如果走到resize,会调用这里重写的transfer
  37. //HashMap里面的transfer是n * m次运算,LinkedHashtable重写后是n + m次运算
  38. void transfer(HashMap.Entry[] newTable) {
  39. int newCapacity = newTable.length;
  40. //直接遍历header链表,HashMap里面是遍历Entry数组
  41. for (Entry<K, V> e = header.after; e != header; e = e.after) {
  42. int index = indexFor(e.hash, newCapacity);
  43. e.next = newTable[index];
  44. newTable[index] = e;
  45. }
  46. }

下面三个图是初始化LinkedHashMap------->添加Entry e1------>添加Entry e2时,LinkedHashMap结构的变化。

六.取数据

Java代码  

  1. //重写了get(Object key)方法
  2. public V get(Object key) {
  3. //1.调用HashMap的getEntry方法得到e
  4. Entry<K, V> e = (Entry<K, V>) getEntry(key);
  5. if (e == null)
  6. return null;
  7. //2.LinkedHashMap牛B的地方
  8. e.recordAccess(this);
  9. return e.value;
  10. }
  11. // 继承了HashMap.Entry
  12. private static class Entry<K, V> extends HashMap.Entry<K, V> {
  13. //1.此方法提供了LRU的实现
  14. //2.通过12两步,把最近使用的当前Entry移到header的before位置,而LinkedHashIterator遍历的方式是从header.after开始遍历,先得到最近使用的Entry
  15. //3.最近使用是什么意思:accessOrder为true时,get(Object key)方法会导致Entry最近使用;put(K key, V value)/putForNullKey(value)只有是覆盖操作时会导致Entry最近使用。它们都会触发recordAccess方法从而导致Entry最近使用
  16. //4.总结LinkedHashMap迭代方式:accessOrder=false时,迭代出的数据按插入顺序;accessOrder=true时,迭代出的数据按LRU顺序+插入顺序
  17. //  HashMap迭代方式:横向数组 * 竖向next链表
  18. void recordAccess(HashMap<K, V> m) {
  19. LinkedHashMap<K, V> lm = (LinkedHashMap<K, V>) m;
  20. //如果使用LRU算法
  21. if (lm.accessOrder) {
  22. lm.modCount++;
  23. //1.从header链表里面移除当前Entry
  24. remove();
  25. //2.把当前Entry移到header的before位置
  26. addBefore(lm.header);
  27. }
  28. }
  29. //让当前Entry从header链表消失
  30. private void remove() {
  31. before.after = after;
  32. after.before = before;
  33. }
  34. }

七.删数据

Java代码  

  1. // 继承了HashMap.Entry
  2. private static class Entry<K, V> extends HashMap.Entry<K, V> {
  3. //LinkedHashMap没有重写remove(Object key)方法,重写了被remove调用的recordRemoval方法
  4. //这个方法的设计也和精髓,也是模板方法模式
  5. //HahsMap remove(Object key)把数据从横向数组 * 竖向next链表里面移除之后(就已经完成工作了,所以HashMap里面recordRemoval是空的实现调用了此方法
  6. //但在LinkedHashMap里面,还需要移除header链表里面Entry的after和before关系
  7. void recordRemoval(HashMap<K, V> m) {
  8. remove();
  9. }
  10. //让当前Entry从header链表消失
  11. private void remove() {
  12. before.after = after;
  13. after.before = before;
  14. }
  15. }

八.LinkedHashMap EntrySet遍历

Java代码  

  1. private abstract class LinkedHashIterator<T> implements Iterator<T> {
  2. //从header.after开始遍历
  3. Entry<K, V> nextEntry = header.after;
  4. Entry<K, V> nextEntry() {
  5. if (modCount != expectedModCount)
  6. throw new ConcurrentModificationException();
  7. if (nextEntry == header)
  8. throw new NoSuchElementException();
  9. Entry<K, V> e = lastReturned = nextEntry;
  10. nextEntry = e.after;
  11. return e;
  12. }
  13. }
  1. 上图中,遍历的结果是先e1然后e2。
  2. accessOrder为true时,get(e1.key)或者put(e1.key, value)一下,则结构1变成e2------e1------header,遍历的结果就是先e2然后e1。

九.总结

  1. LinkedHashMap继承HashMap,结构2里数据结构的变化交给HashMap就行了。
  2. 结构1里数据结构的变化就由LinkedHashMap里重写的方法去实现。
  3. 简言之:LinkedHashMap比HashMap多维护了一个链表。

http://zy19982004.iteye.com/blog/1663303

LinkedHashMap保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的.也可以在构造时用带参数,按照应用次数排序。在遍历的时候会比HashMap慢,不过有种情况例外,当HashMap容量很大,实际数据较少时,遍历起来可能会比LinkedHashMap慢,因为LinkedHashMap的遍历速度只和实际数据有关,和容量无关,而HashMap的遍历速度和他的容量有关。

如果需要输出的顺序和输入的相同,那么用LinkedHashMap 可以实现,它还可以按读取顺序来排列.

优点:可前后查询
缺点:效率没有hashmap高

LinkedHashMap是HashMap的一个子类,保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的.也可以在构造时用带参数,按照应用次数排序。
在遍历的时候会比HashMap慢,不过有种情况例外,当HashMap容量很大,实际数据较少时,遍历起来可能会比LinkedHashMap慢,因为LinkedHashMap的遍历速度只和实际数据有关,和容量无关,而HashMap的遍历速度和他的容量有关

LinkedHashMap输出时其元素是有顺序的,而HashMap输出时是随机的,如果Map映射比较复杂而又要求高效率的话,最好使用LinkedHashMap,但是多线程访问的话可能会造成不同步,所以要用Collections.synchronizedMap来包装一下,从而实现同步。其实现一般为:
    Map<String String> map = Collections.synchronizedMap(new LinkedHashMap(<String String));

LinkedHashMap和HashMap的区别在于它们的基本数据结构上,看一下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);
    }
    ...
}

列一下Entry里面有的一些属性吧:

1、K key

2、V value

3、Entry<K, V> next

4、int hash

5、Entry<K, V> before

6、Entry<K, V> after

其中前面四个,也就是红色部分是从HashMap.Entry中继承过来的;后面两个,也就是蓝色部分是LinkedHashMap独有的。不要搞错了next和before、After,next是用于维护HashMap指定table位置上连接的Entry的顺序的,before、After是用于维护Entry插入的先后顺序的

还是用图表示一下,列一下属性而已:

public LinkedHashMap (int initialCapacity, float loadFactor, boolean accessOrder);
 initialCapacity   初始容量
 loadFactor    加载因子,一般是 0.75f
 accessOrder   false 基于插入顺序  true  基于访问顺序(get一个元素后,这个元素被加到最后,使用了LRU 最  近最少被使用的调度算法)
如 boolean accessOrder = true;
      Map<String, String> m = new LinkedHashMap<String, String>(20, .80f,  accessOrder  );
      m.put("1", "my"));
      m.put("2", "map"));
      m.put("3", "test"));
      m.get("1");
      m.get("2");
      Log.d("tag",  m);
     若 accessOrder == true;  输出 {3=test, 1=my, 2=map}
         accessOrder == false;  输出 {1=my, 2=map,3=test}

顾名思义,LRUCache就是基于LRU算法的Cache(缓存),这个类继承自LinkedHashMap,而类中看到没有什么特别的方法,这说明LRUCache实现缓存LRU功能都是源自LinkedHashMap的。LinkedHashMap可以实现LRU算法的缓存基于两点:

1、LinkedList首先它是一个Map,Map是基于K-V的,和缓存一致

2、LinkedList提供了一个boolean值可以让用户指定是否实现LRU

那么,首先我们了解一下什么是LRU:LRU即Least Recently Used,最近最少使用,也就是说,当缓存满了,会优先淘汰那些最近最不常访问的数据。比方说数据a,1天前访问了;数据b,2天前访问了,缓存满了,优先会淘汰数据b。

我们看一下LinkedList带boolean型参数的构造方法:

public LinkedHashMap(int initialCapacity,
         float loadFactor,
                     boolean accessOrder) {
    super(initialCapacity, loadFactor);
    this.accessOrder = accessOrder;
}

就是这个accessOrder,它表示:

(1)false,所有的Entry按照插入的顺序排列

(2)true,所有的Entry按照访问的顺序排列

第二点的意思就是,如果有1 2 3这3个Entry,那么访问了1,就把1移到尾部去,即2 3 1。每次访问都把访问的那个数据移到双向队列的尾部去,那么每次要淘汰数据的时候,双向队列最尾的那个数据不就是最不常访问的那个数据了吗?换句话说,双向链表最尾的那个数据就是要淘汰的数据。

"访问",这个词有两层意思:

1、根据Key拿到Value,也就是get方法

2、修改Key对应的Value,也就是put方法

http://www.mamicode.com/info-detail-1154295.html

时间: 2024-11-05 01:57:37

LinkedHashMap相关信息介绍(转)的相关文章

支付宝手机网页即时到账接口(1)之相关信息介绍

前言 博主近几周在写一个微商城的项目,里面有涉及到付款模块,发现原来电脑端的支付宝接口与手机端的接口不一致(原来的电脑端平台非本人开发).然后便去支付宝商家服务下载集成开发包. 该集成开发包包含即时到账批量退款有密接口.商户接入支付宝收银台界面展示标准-无线和手机网页即时到账接口三个文件夹. 1.即时到账批量退款有密接口 demo 该文件夹下有8个示例项目,分为4种不同语言(ASP.CSHARP.JAVA.PHP)和2种不同编码(GBK.UTF-8) 更新日志 即时到账批量退款有密接口(refu

Linux基础--进程管理相关命令介绍(2)

本文主要介绍了Linux中进程管理的相关命令,涉及到的主要命令有top,vmstat等. (1)top ①功能:用来查看CPU,内存以及进程的状态. ②用例: ③相关注释: load average表示负载,三个数值分别表示第1分钟,第5分钟,第10分钟 Cpu中us表示用户空间程序占用百分比,sy表示内核模式占用百分比,ni表示调整NICE值所占用的    CPU百分比,id表示CPU的空闲比例,wa表示等待磁盘IO完成所占用的时间比例,hi表示硬件中断占     据的百分比,si表示软中断所

MyEclipse相关用法介绍

MyEclipse相关用法介绍 ================================================================================ 编辑: Ctrl+Shift+L      显示所有快捷键 Ctrl+K        参照选中的词(Word)快速定位到下一个 Ctrl+Shift+K      参照选中的词(Word)快速定位到上一个 Ctrl+O         快速显示OutLine Ctrl+T        快速显示当前类的

内存问题排查手段及相关文件介绍

5.内存问题排查手段及相关文件介绍[重点] 对于内存问题排查,或者OOM问题排查,一般会涉及到如下文件,下面将如下文件的分析和设置介绍一下,这也是本文档的重点,后面排查内存信息还是要根据这些文件信息来排查.其实未必是有内存泄露,也可能是一些策略有问题,比如线程数目的增加,buffer的申请.释放时间交集等. 5.1 /proc/sys/vm/min_free_kbytes min_free_kbytes用来确定系统开始回收内存的阀值,控制系统的空闲内存.值越高,内核越早开始回收内存,空闲内存越高

网络视频相关技术介绍

AnyChat音视频互动开发平台(SDK)是一套跨平台的即时通讯解决方案,基于先进的H.264视频编码标准.AAC音频编码标准与P2P技术,支持高清视频,整合了佰锐科技在音视频编码.多媒体通讯领域领先的开发技术和丰富的产品经验而设计的高质量.宽适应性.分布式.模块化的网络音视频互动平台.        AnyChat音视频互动开发平台(SDK)包含了音视频处理模块(采集.编解码).流媒体管理模块(丢包重传.抖动平滑.动态缓冲).流媒体播放模块(多路混音.音视频同步)以及P2P网络模块(NAT穿透

数据库相关中间件介绍

数据库相关中间件介绍 详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt412 这里主要介绍互联网行业内有关数据库的相关中间件.数据库相关平台主要解决以下三个方面的问题: 为海量前台数据提供高性能.大容量.高可用性的访问 为数据变更的消费提供准实时的保障 高效的异地数据同步 应用层通过分表分库中间件访问数据库,包括读操作(Select)和写操作(update, insert和delete等,DDL, DCL).写操作会在数据

ManagementClass类解析和C#如何获取硬件的相关信息

在.NET的项目中,有时候需要获取计算机的硬件的相关信息,在C#语言中需要利用ManagementClass这个类来进行相关操作. 现在先来介绍一下ManagementClass类,首先看一下类的继承结构: 现在看一下MSDN对ManagementClass类的解释,对表示一个通用信息模型 (CIM) 管理类. 管理类是 WMI 类,如 Win32_LogicalDisk, ,该类型可表示一个磁盘驱动器,并 Win32_Process, ,它表示的进程 Notepad.exe 等. 此类的成员可

学习NGUI前的准备NGUI的相关信息

学习NGUI前的准备NGUI的相关信息 第1章  学习NGUI前的准备 NGUI是Unity最重要的插件,在Unity资源商店(Asset Store)的付费排行榜中始终名列前茅,如图1-1所示.本章作为本书的第一讲,内容主要包括以下几个部分本文选自NGUI全面实践教程: q  NGUI的购买/下载和导入过程: q  熟悉NGUI资源的构成: q  熟悉NGUI主菜单的各菜单项: q  熟悉NGUI支持的鼠标快捷操作: 本章的内容可以让读者更加全面的认识NGUI,同时也为本书后面章节的讲解做铺垫

CloudStack(一)简介及相关理论介绍

简介 CloudStack(cloudstack.apache.org)是IaaS类型云计算的一种开源解决方案,同类的解决方案有OpenStack.OpenNebula等,CloudStack是以java语言所研发并具有高可用性.可扩展性.丰富的UI功能.Hypervisor 的多样性等等..(更多请见http://www.cloudstack-china.org/2013/11/2702.html),它可以帮助用户利用自己的硬件提供类似于Amazon EC2那样的公共云服务.CloudStac