转载:http://blog.csdn.net/luanlouis/article/details/43017071

Map作为键值对Entry<K,V>的的容器,对其内部 键值对Entry<K,V> 的遍历总归是要有一个顺序的。

本文重点讨论HashMap及其子类LinkedHashMap的遍历机制,总结出两者的特点和适用情况。

1.HashMap的遍历机制

HashMap 提供了两个遍历访问其内部元素Entry<k,v>的接口:

1.       Set<Map.Entry<K,V>> entrySet()     返回此映射所包含的映射关系的 Set 视图。

2.       Set<K> keySet()              返回此映射中所包含的键的 Set 视图。

实际上,第二个借口表示的Key的顺序,和第一个接口返回的Entry顺序是对应的,也就是说:这两种接口对HashMap的元素遍历的顺序相相同的。  那么,HashMap遍历内部Entry<K,V> 的顺序是什么呢? 搞清楚这个问题,先要知道其内部结构是怎样的。

HashMap内部对键值对的存储结构使用的是数组+链表的形式。其结构如下图所示:

HashMap内部Entry<K,V>的遍历顺序:

对Entry[] table 数组,从index=0开始,依次遍历table[i] 上的链表上的Entry对象。

由于HashMap在存储Entry对象的时候,是根据Key的hash值判定存储到Entry[] table数组的哪一个索引值表示的链表上,所以笼统地说就是:使用hashMap.put(Key key,Value value)会将 对应的Entry<Key,Value>对象随机地分配到某个Entry[] table数组的元素表示的链表上。换一句话说就是:

 对HashMap遍历Entry对象的顺序和Entry对象的存储顺序之间没有任何关系。

但是,我们有时候想要遍历HashMap的元素Entry的顺序和其存储的顺序一致,HashMap显然不能满足条件了。而LinkedHashMap则可以满足这个需要。

2. LinkedHashMap 的遍历机制

LinkedHashMap 是HashMap的子类,它可以实现对容器内Entry的存储顺序和对Entry的遍历顺序保持一致。

为了实现这个功能,LinkedHashMap内部使用了一个Entry类型的双向链表用这个双向链表记录Entry的存储顺序。当需要对该Map进行遍历的时候,实际上是遍历的是这个双向链表。

LinkedHashMap内部使用的LinkedHashMap.Entry类继承自 Map.Ent ry类,在其基础上增加了LinkedHashMap.Entry类型的两个字段,用来引用该Entry在双向链表中的前面的Entry对象和后面的Entry对象。

它的内部会在 Map.Entry 类的基础上,增加两个Entry类型的引用:before,after。LinkedHashMap使用一个双向连表,将其内部所有的Entry串起来。

我们将通过以下例子,来了解内部双向链表是怎样构造的:

  1. LinkedHashMap linkedHashMap = new LinkedHashMap();
  2. linkedHashMap.put("name","louis");
  3. linkedHashMap.put("age","24");
  4. linkedHashMap.put("sex","male");

上述的代码除了会将对应的Entry对象放置到在Entry[] table 表示的数组链表中外,还会将该Entry对象添加到其内部维护的双向链表中。对应的LinkedHashMap内部的双向链表变化如下:

对LinkedHashMap进行遍历的策略:

从 header.after 指向的Entry对象开始,然后一直沿着此链表 遍历下去,直到某个entry.after == header 为止,完成遍历。

由此,就可以保证遍历LinkedHashMap内元素的顺序,就是Entry插入到LinkedHashMap中的顺序。

将上面代码中定义的linkedHashMap 遍历输出,会发现遍历的顺序跟插入的顺序完全一致:

  1. Iterator<Map.Entry> iterator= linkedHashMap.entrySet().iterator();
  2. while(iterator.hasNext())
  3. {
  4. Map.Entry entry = iterator.next();
  5. System.out.println(entry.getKey()+":"+entry.getValue());
  6. }

结果输出:

根据Entry<K,V>插入LinkedHashMap的顺序进行遍历的方式叫做:按插入顺序遍历

另外,LinkedHashMap还支持一种遍历顺序,叫做:Get读取顺序

 如果LinkedHashMap的这个Get读取遍历顺序开启,那么,当我们在LinkedHashMap上调用get(key) 方法时,会导致内部 key对应的Entry在双向链表中的位置移动到双向链表的最后。

比如,如果当前LinkedHashMap内部的双向链表的情况如下:

相关代码如下:

  1. //默认情况下LinkedHashMap的遍历模式是插入模式,如果想显式地指定为get读取模式,那么要将
  2. //其构造方法的参数置为true,(false 表示的是插入模式)
  3. LinkedHashMap linkedHashMap = new LinkedHashMap(16, (float) 0.75,true);
  4. linkedHashMap.put("name","louis");
  5. linkedHashMap.put("age","24");
  6. linkedHashMap.put("sex","male");
  7. linkedHashMap.get("name");//get()方法调用,导致对应的entry移动到双向链表的最后位置
  8. Iterator<Map.Entry> iterator= linkedHashMap.entrySet().iterator();
  9. while(iterator.hasNext())
  10. {
  11. Map.Entry entry = iterator.next();
  12. System.out.println(entry.getKey()+":"+entry.getValue());
  13. }

3. 总结

1.HashMap对元素的遍历顺序跟Entry插入的顺序无关,而LinkedHashMap对元素的遍历顺序可以跟Entry<K,V>插入的顺序保持一致。

2.当LinkedHashMap处于Get获取顺序遍历模式下,当执行get() 操作时,会将对应的Entry<k,v>移到遍历的最后位置。

3.LinkedHashMap处于按插入顺序遍历的模式下,如果新插入的<key,value> 对应的key已经存在,对应的Entry在遍历顺序中的位置并不会改变。

4. 除了遍历顺序外,其他特性HashMap和LinkedHashMap基本相同。

LinkedHasMap实现原理的更多相关文章

  1. 奇异值分解(SVD)原理与在降维中的应用

    奇异值分解(Singular Value Decomposition,以下简称SVD)是在机器学习领域广泛应用的算法,它不光可以用于降维算法中的特征分解,还可以用于推荐系统,以及自然语言处理等领域.是 ...

  2. node.js学习(三)简单的node程序&&模块简单使用&&commonJS规范&&深入理解模块原理

    一.一个简单的node程序 1.新建一个txt文件 2.修改后缀 修改之后会弹出这个,点击"是" 3.运行test.js 源文件 使用node.js运行之后的. 如果该路径下没有该 ...

  3. 线性判别分析LDA原理总结

    在主成分分析(PCA)原理总结中,我们对降维算法PCA做了总结.这里我们就对另外一种经典的降维方法线性判别分析(Linear Discriminant Analysis, 以下简称LDA)做一个总结. ...

  4. [原] KVM 虚拟化原理探究(1)— overview

    KVM 虚拟化原理探究- overview 标签(空格分隔): KVM 写在前面的话 本文不介绍kvm和qemu的基本安装操作,希望读者具有一定的KVM实践经验.同时希望借此系列博客,能够对KVM底层 ...

  5. H5单页面手势滑屏切换原理

    H5单页面手势滑屏切换是采用HTML5 触摸事件(Touch) 和 CSS3动画(Transform,Transition)来实现的,效果图如下所示,本文简单说一下其实现原理和主要思路. 1.实现原理 ...

  6. .NET Core中间件的注册和管道的构建(1)---- 注册和构建原理

    .NET Core中间件的注册和管道的构建(1)---- 注册和构建原理 0x00 问题的产生 管道是.NET Core中非常关键的一个概念,很多重要的组件都以中间件的形式存在,包括权限管理.会话管理 ...

  7. python自动化测试(2)-自动化基本技术原理

    python自动化测试(2) 自动化基本技术原理 1   概述 在之前的文章里面提到过:做自动化的首要本领就是要会 透过现象看本质 ,落实到实际的IT工作中就是 透过界面看数据. 掌握上面的这样的本领 ...

  8. CRC、反码求和校验 原理分析

    3月份开始从客户端转后台,算是幸运的进入全栈工程师的修炼阶段.这段时间一边是老项目的客户端加服务器两边的维护和交接,一边是新项目加加加班赶工,期间最长经历了连续工作三天只睡了四五个小时的煎熬,人生也算 ...

  9. 菜鸟学Struts2——Struts工作原理

    在完成Struts2的HelloWorld后,对Struts2的工作原理进行学习.Struts2框架可以按照模块来划分为Servlet Filters,Struts核心模块,拦截器和用户实现部分,其中 ...

随机推荐

  1. 用mac自带的safari浏览器下载excel文件后面自带了.exe后缀

    将 Content-Type 设为 application/vnd.ms-excel

  2. python实现的基于TCP的JSON数据通信

    用Python写的一个多线程TCP通信实例,实现了JSON数据的传输. 闲言少述,直接上代码 ​ 一.client #!/usr/bin/env python # -*- coding:utf-8 - ...

  3. C#高级编程四十九天----队列

    队列 1.Queue定义 System.Collections.Queue类表示对象的先进先出集合,存储在Queue(队列)中的对象在一端插入,从还有一端移除. 2.长处 (1).能对集合进行顺序处理 ...

  4. requestFocusFromTouch , requestFocus

    /*if(userNameEditText.isInTouchMode()){ userNameEditText.requestFocusFromTouch(); }else{ userNameEdi ...

  5. 瀑布流AJAX无刷新加载数据列表--当页面滚动到Id时再继续加载数据

    瀑布流加载显示数据,在当下已经用的很普遍,尤其是我们在做网上商城时,在产品列表页面已经被普遍使用. 对于实现瀑布流布局的解决方案主要有以下两种方式: 1.对每一条显示数据使用绝对定位+浮动的方式,这样 ...

  6. Cookie中图片的浏览记录与cookie读取servle时路径的设置(文字描述)

    public class ShowServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpSer ...

  7. HTTP请求类型详解

    HTTP(HyperText Transfer Protocol)是一套计算机通过网络进行通信的规则.计算机专家设计出HTTP,使HTTP客户(如Web浏览器)能够从HTTP服务器(Web服务 器)请 ...

  8. docker daemonize sshd service

    # 第一个-d表示让容器在后台运行 # 末尾的-D表示启动ssh的daemon模式,不然容器启动后立刻就变为停止状态了 docker run -d NAME/VERSION /etc/init.d/s ...

  9. javascript进击(七)Ajax

    AJAX AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML). AJAX 不是新的编程语言,而是一种使用现有标准的新方法. AJA ...

  10. nyoj 33 蛇形填数

    蛇形填数 时间限制:3000 ms  |            内存限制:65535 KB 难度:3   描述 在n*n方陈里填入1,2,...,n*n,要求填成蛇形.例如n=4时方陈为: 10 11 ...