关于Java的HashMap.entrySet(),文档是这样描述的:这个方法返回一个Set,这个Set是HashMap的视图,对Map的操作会在Set上反映出来,反过来也是。原文是

Returns a Set view of the mappings contained in this map. The set is backed by the map, so changes to the map are reflected in the set, and vice-versa.

本文通过源码简单分析这一功能的实现。

首先要简单介绍一下HashMap的内部存储。我们知道,Map是用来存储key-value类型数据的,一个<k, v>对在Map的接口定义中被定义为Entry,HashMap内部实现了Entry接口。HashMap内部维护一个Entry数组。

transient Entry[] table;

当put一个新元素的时候,根据key的hash值计算出对应的数组下标。数组的每个元素是一个链表的头指针,用来存储具有相同下标的Entry。

Entry[] table
+---+
| 0 | -> entry_0_0 -> entry_0_1 -> null
+---+
| 1 | -> null
+---+
| | ... |n-1| -> entry_n-1_0 -> null
+---+

entrySet()方法返回的是一个特殊的Set,定义为HashMap的内部私有类

private final class EntrySet extends AbstractSet<Map.Entry<K,V>>

主要看一下这个Set的iterator()方法。这个方法很简单,返回一个EntryIterator类型的实例。EntryIterator类型是泛型HashIterator<T>的一个子类,这个类的内容很简单,唯一的代码是在next()函数中调用了HashIteratornextEntry()方法。所以,重点就变成了分析nextEntry()方法。上述过程见下面的图示

HashMap
|- table <------------------------------------\
\+ entrySet() |iterates
| HashMap.HashIterator<T> |
|returns ^ \- nextEntry()
V - ^
HashMap.EntrySet | |
\- iterator() |extends |
| | |
| instantiats | |calls
\----------> HashMap.EntryIterator |
\- next() /

HashIterator通过遍历table数组,实现对HashMap的遍历。内部维护几个变量:index记录当前在table数组中的下标,current用来记录当前在table[index]这个链表中的位置,next指向current的下一个元素。nextEntry()的完整代码如下:

final Entry<K,V> nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
Entry<K,V> e = next;
if (e == null)
throw new NoSuchElementException(); if ((next = e.next) == null) {
Entry[] t = table;
while (index < t.length && (next = t[index++]) == null)
;
}
current = e;
return e;
}

第一个if用来判断在多线程的情况下是否出现并发错误,这里暂时不讨论。如果next不是null,那么返回并更新next。更新方法是第三个if的内容:如果当前链表还没有结束,则简单的把next向后移一个;否则在table中查找下一个非空的slot。

总结一下,HashMap的entrySet()方法返回一个特殊的Set,这个Set使用EntryIterator遍历,而这个Iterator则直接操作于HashMap的内部存储结构table上。通过这种方式实现了“视图”的功能。整个过程不需要任何辅助存储空间。

p.s. 从这一点也可以看出为什么entrySet()是遍历HashMap最高效的方法,原因很简单,因为这种方式和HashMap内部的存储方式是一致的。

简单分析Java的HashMap.entrySet()的实现的更多相关文章

  1. 透过byte数组简单分析Java序列化、Kryo、ProtoBuf序列化

    序列化在高性能网络编程.分布式系统开发中是举足轻重的之前有用过Java序列化.ProtocolBuffer等,在这篇文章这里中简单分析序列化后的byte数组观察各种序列化的差异与性能,这里主要分析Ja ...

  2. 简单分析Java中审批业务流程业务原理

  3. java基础---->hashMap的简单分析(一)

    HashMap是一种十分常用的数据结构对象,可以保存键值对.它在项目中用的比较多,今天我们就来学习一下关于它的知识. HashMap的简单使用 一.hashMap的put和get方法 Map<S ...

  4. Java源码分析:关于 HashMap 1.8 的重大更新(转载)

    http://blog.csdn.net/carson_ho/article/details/79373134 前言 HashMap 在 Java 和 Android 开发中非常常见 而HashMap ...

  5. 【Java】HashMap源码分析——常用方法详解

    上一篇介绍了HashMap的基本概念,这一篇着重介绍HasHMap中的一些常用方法:put()get()**resize()** 首先介绍resize()这个方法,在我看来这是HashMap中一个非常 ...

  6. 【Java】HashMap源码分析——基本概念

    在JDK1.8后,对HashMap源码进行了更改,引入了红黑树.在这之前,HashMap实际上就是就是数组+链表的结构,由于HashMap是一张哈希表,其会产生哈希冲突,为了解决哈希冲突,HashMa ...

  7. java基础---->hashSet的简单分析(一)

    对于HashSet而言,它是基于HashMap实现的,底层采用HashMap来保存元素的.今天我们就简单的分析一下它的实现.人生,总会有不期而遇的温暖,和生生不息的希望. HashSet的简单分析 一 ...

  8. Java动态替换InetAddress中DNS的做法简单分析1

    在java.net包描述中, 简要说明了一些关键的接口. 其中负责networking identifiers的是Addresses. 这个类的具体实现类是InetAddress, 底层封装了Inet ...

  9. java 中 “文件” 和 “流” 的简单分析

    java 中 FIle 和 流的简单分析 File类 简单File 常用方法 创建一个File 对象,检验文件是否存在,若不存在就创建,然后对File的类的这部分操作进行演示,如文件的名称.大小等 / ...

随机推荐

  1. XAF Excel数据导入模块使用说明与源码

    我实现了XAF项目中Excel数据的导入,使用Devexpress 新出的spreadsheet控件,可能也不新了吧:D 好,先看一下效果图:下图是Web版本的. 下面是win版: 功能说明: 支持从 ...

  2. Xstream 学习地址

    http://forestqqqq.iteye.com/category/301129

  3. JSP学习——语法

    JSP模版元素 JSP表达式 JSP脚本片断 JSP注释JSP指令JSP标签 JSP内置对象如何查找JSP页面中的错误 1:JSP模版元素 : JSP页面中的HTML内容称之为JSP模版元素. JSP ...

  4. apiCloud结合layer实现动态数据弹出层

    css /** 我的二维码 begin **/ .aui-list .wechat-media { width: 3rem; } .wechat-middle { padding: 1.3em 3.2 ...

  5. Java源码初学_HashSet&LinkedHashSet

    一.概述 HashSet是建立在HashMap的基础上的,其内部存在指向一个HashMap对象的引用,操作HashSet实际上就是操作HashMap,而HashMap中所有键的值都指向一个叫做Dumm ...

  6. D3.js 第一个程序 HelloWorld

    一.HTML 是怎么输出 HelloWorld 的 <html> <head> <meta charset="utf-8"> <title ...

  7. D3.js 理解 Update、Enter、Exit

    Update.Enter.Exit 是 D3 中三个非常重要的概念,它处理的是当选择集和数据的数量关系不确定的情况. 一.什么是 Update.Enter.Exit svg.selectAll(&qu ...

  8. TCP 连接建立和断开,以及状态转换

    1. TCP报文结构 TCP是一种可靠.面向连接.全双工的传输层协议,其报文格式如下所示:      源端口.目的端口:16位长.标识出远端和本地的端口号.     顺序号:32位长.表明了发送的数据 ...

  9. redirect模块的秘密

    所有的redirect记录都在config/url_directs下面, 但是某个node/edit的redirect记录只包含redirect到自己的记录,且不验证url的合理性. 现在比如,a跳转 ...

  10. Lua中点和冒号的区别

    在使用lua设计类时'.'和':'的区别主要在于使用'.'必须手动加self参数,使用':',可以隐藏这个参数,使用'.'调用使用':'定义的函数时,要注意,函数的第一个参数为self,如 funct ...