hashmap的entryset()返回懒加载的视图,不复制数据,所有操作委托给原map;遍历时按数组索引升序扫描非空桶及其链表/红黑树节点,支持安全的it.remove(),但禁止遍历中直接调用map.remove()。

Java HashMap 的 entrySet() 返回的是一个“视图”(View),它不复制底层数据,而是直接映射到哈希表的内部结构。遍历时实际访问的是 HashMap 的 Node 数组及其链表/红黑树节点,但对外表现为 Set
entrySet 是如何构建的
HashMap 中 entrySet 是一个懒加载的内部类实例(EntrySet),首次调用 entrySet() 时才创建。它继承自 AbstractSet,重写了 iterator()、size()、contains() 等方法,所有操作都委托给 HashMap 本身。
关键点:
- 不持有独立数据副本,所有增删改查都直接影响原 HashMap
-
iterator()返回的是EntryIterator(内部迭代器类),它按数组索引 + 链表/树顺序逐个扫描非空桶 - 遍历时跳过 null 桶,对每个非空桶遍历其链表或红黑树节点
遍历 entrySet 的典型方式
最常用的是增强 for 循环或显式获取 Iterator:
for (Map.Entry<string integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + "=" + entry.getValue());
}</string>
等价于:
Iterator<map.entry integer>> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<string integer> entry = it.next();
// 使用 entry
}</string></map.entry>
注意:it.remove() 是安全的,会同步删除 HashMap 中对应键值对;但不能在遍历中直接调用 map.remove(key),否则会触发 fail-fast 机制抛出 ConcurrentModificationException。
遍历过程中的底层行为
EntryIterator 的 next() 方法核心逻辑是:
- 从当前数组索引开始,找到第一个非空桶(即 table[i] != null)
- 返回该桶的第一个节点(Node 或 TreeNode)
- 下次调用时,先尝试取当前桶的 next 节点;若为 null,则向后查找下一个非空桶
- 整个过程按 table 数组下标升序,桶内按插入/树序遍历
例如:table = [null, nodeA→nodeB, null, nodeC],遍历顺序是 nodeA → nodeB → nodeC。
注意事项与常见误区
使用 entrySet 遍历时需注意:
- 遍历期间修改 HashMap 结构(如 put/remove)会导致迭代器失效 —— 这是通过 modCount 和 expectedModCount 检查实现的
- Entry 对象是“活”的:修改其 value(通过
entry.setValue(v))会直接更新 HashMap 中对应值(JDK 8+ 支持) - key 不可变:Entry 的 getKey() 返回的是原始引用,但 key 本身不可通过 Entry 修改(除非 key 自身可变且未重写 equals/hashCode)
- 并发场景下,entrySet 视图不保证线程安全,需外部同步或使用 ConcurrentHashMap
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











