[Java基础要义]HashMap、LinkedHashMap元素遍历机制探讨

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

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

 CSDN-2014博客之星投票啦

CSDN-2014博客之星   评选开始啦,如果您觉得我的文章对您有所帮助,请您点击左边栏的图片投我一票,您的支持是我分享知识的强大动力!

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串起来。

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

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

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

对LinkedHashMap进行遍历的策略:

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

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

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

        Iterator<Map.Entry> iterator= linkedHashMap.entrySet().iterator();

        while(iterator.hasNext())
        {
            Map.Entry entry = iterator.next();
            System.out.println(entry.getKey()+":"+entry.getValue());
        }

结果输出:

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

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

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

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

相关代码如下:

        //默认情况下LinkedHashMap的遍历模式是插入模式,如果想显式地指定为get读取模式,那么要将
        //其构造方法的参数置为true,(false 表示的是插入模式)
        LinkedHashMap linkedHashMap = new LinkedHashMap(16, (float) 0.75,true);

        linkedHashMap.put("name","louis");
        linkedHashMap.put("age","24");
        linkedHashMap.put("sex","male");
        linkedHashMap.get("name");//get()方法调用,导致对应的entry移动到双向链表的最后位置

        Iterator<Map.Entry> iterator= linkedHashMap.entrySet().iterator();

        while(iterator.hasNext())
        {
            Map.Entry entry = iterator.next();
            System.out.println(entry.getKey()+":"+entry.getValue());
        }

3. 总结

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

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

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

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

时间: 2024-10-13 02:21:57

[Java基础要义]HashMap、LinkedHashMap元素遍历机制探讨的相关文章

[Java基础要义] HashMap的设计原理和实现分析

HashMap在Java开发中有着非常重要的角色地位,每一个Java程序员都应该了解HashMap. 本文主要从源码角度来解析HashMap的设计思路,并且详细地阐述HashMap中的几个概念,并深入探讨HashMap的内部结构和实现细节,讨论HashMap的性能问题,并且在文中贯穿着一些关于HashMap常见问题的讨论. 读完本文,你会了解到:     1. HashMap的设计思路和内部结构组成 2. HashMap中的一些概念: 什么是阀值?为什么会有阀值?什么是加载因子?它们有什么作用?

java基础 之 HashMap统计csv文件的单词

一:知识补充( 这个HashMap Map 和 c++的Map还是有很大的区别的,区别之大让人瞠目结舌,当然两者的作用是一致的,但是函数名称出入很大,就连iterator区别也很大的 ) (1)HashMap 和 HashTable的区别(c++中只有map木有hashmap的) HashMap不是线程安全的 hastmap是一个接口 是map接口的子接口,是将键映射到值的对象,其中键和值都是对象,并且不能包含重复键,但可以包含重复值.HashMap允许null key和null value,而

【Java基础】——HashMap设计原理&amp;实现分析

HashMap在Java开发中有着非常重要的角色地位,每一个Java程序员都应该了解HashMap. 本文主要从源码角度来解析HashMap的设计思路,并且详细地阐述HashMap中的几个概念,并深入探讨HashMap的内部结构和实现细节,讨论HashMap的性能问题,并且在文中贯穿着一些关于HashMap常见问题的讨论. 读完本文,你会了解到:   1. HashMap的设计思路和内部结构组成 2. HashMap中的一些概念: 什么是阀值?为什么会有阀值?什么是加载因子?它们有什么作用? 3

Java基础:HashMap假死锁问题的测试、分析和总结

前言 前两天在公司的内部博客看到一个同事分享的线上服务挂掉CPU100%的文章,让我联想到HashMap在不恰当使用情况下的死循环问题,这里做个整理和总结,也顺便复习下HashMap. 直接上测试代码 由于机器配置和性能不同,测试出效果的线程数和put数量也各不相同 public class HashMapInfiniteLoopTest { /** * 基于JDK1.7测试HashMap在多线程环境下假死锁的情况 * JDK1.8的HashMap实现跟1.7的比较有很大的变化,已不存在这样的问

Java基础:HashMap和HashSet

转载请注明出处:jiq?钦's technical Blog 一.HashMap HashMap,基于散列(哈希表)存储"Key-Value"对象引用的数据结构. 存入的键必须具备两个关键函数: (1)equals():  判断两个Key是否相同,用来保证存入的Key的唯一性: (2)hashCode(): 根据k-v对象的Key来计算其引用在散列表中存放的位置: HashMap底层结构是一个数组: transientEntry<K,V>[] table 而其中Entry&

Java基础知识总结之垃圾回收机制

垃圾回收机制 Java死亡垃圾回收机制是Java语言的重要功能之一.当程序创建对象,数组等引用类型对象时,系统会自动在内存区为之分配一块内存,对象就保存在这块内存区内,当这块内存不再被任何变量引用时,这块内存就成为了垃圾,等待垃圾回收机制进行回收. 垃圾回收机制的特征 1.垃圾回收机制只负责回收堆内存中的对象,不会回收任何物理资源(例如数据库连接.网络连接.IO流等资源). 2.程序无法精准控制垃圾回收的运行,垃圾回收会合适的时候进行.当对象永久性地失去引用后,系统就会在合适的时候回收塔

JAVA基础篇—HashMap

/class Depositor package 银行储户; public class Depositor { private String id; private String name; private double balance; public Depositor(String id,String name,double bal) { // TODO Auto-generated constructor stub super(); this.id=id; this.name=name;

HashMap元素遍历的顺序问题

Java中关于HashMap的元素遍历的顺序问题   发现得到的元素不是按照之前加入HashMap的顺序输出的,这个问题我之前倒是没有注意过,后来上网查了一下原因,发现是:HashMap散列图.Hashtable散列表是按“有利于随机查找的散列(hash)的顺序”.并非按输入顺序.遍历时只能全部输出,而没有顺序.甚至可以rehash()重新散列,来获得更利于随机存取的内部顺序.总之,遍历HashMap或Hashtable时不要求顺序输出,即与顺序无关.

Java HashMap 如何正确遍历并删除元素

(一)HashMap的遍历 HashMap的遍历主要有两种方式: 第一种采用的是foreach模式,适用于不需要修改HashMap内元素的遍历,只需要获取元素的键/值的情况. HashMap<K, V> myHashMap; for (Map.entry<K, V> item : myHashMap.entrySet()){ K key = item.getKey(); V val = item.getValue(); //todo with key and val //WARNI