您的位置:首页 > 编程语言 > Java开发

简单分析Java的HashMap.entrySet()的实现

2014-06-16 13:37 323 查看
关于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()
函数中调用了
HashIterator
nextEntry()
方法。所以,重点就变成了分析
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内部的存储方式是一致的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: