keySet()遍历键后需用get()取值,因它只返回键集合;推荐entrySet()遍历以避免重复哈希查找,且迭代中删除须用Iterator.remove()保证安全。

用 keySet() 遍历键并取值,最直接的方式是配合 get()
Java 的 HashMap 的 keySet() 返回一个 Set<k></k>,里面只含键,不含值。想根据键拿到值,必须再调用 map.get(key)。这不是“额外开销”,而是设计使然——keySet() 本身不携带映射关系。
常见错误是误以为 keySet() 返回的集合里能直接访问值,比如写成 entry.getValue()(但 keySet() 里根本没有 Entry)。
- 推荐写法(简洁、可读性好):
for (String key : map.keySet()) { String value = map.get(key); System.out.println(key + " → " + value); } - 如果需要同时操作键和值,且性能敏感(尤其是大 Map),应改用
entrySet()遍历,避免重复哈希查找 -
keySet()是弱一致视图:遍历时若 Map 被并发修改,可能抛ConcurrentModificationException(除非用ConcurrentHashMap或加锁)
为什么不用 keySet().toArray() 再循环?
有人会先把键转成数组再遍历,例如 map.keySet().toArray()。这不仅多一次内存拷贝(O(n) 时间 + O(n) 空间),还丢失了泛型信息(返回 Object[]),强制类型转换易出错。
- 错误示例:
Object[] keys = map.keySet().toArray(); for (Object k : keys) { String key = (String) k; // 潜在 ClassCastException String v = map.get(key); } - 除非你明确需要数组(比如传给某个只接受
String[]的旧 API),否则绕过keySet()直接迭代毫无必要 - 注意:
keySet()迭代顺序不保证(除非用LinkedHashMap)
遍历时修改键集会怎样?
不能在用 keySet() 迭代的同时调用 map.remove(key) 或 map.put(),否则下一次 next() 调用会立即抛 ConcurrentModificationException。
- 安全删除方式(使用迭代器):
Iterator<String> iter = map.keySet().iterator(); while (iter.hasNext()) { String key = iter.next(); if (shouldRemove(key)) { iter.remove(); // ✅ 安全 } } -
iter.remove()删除的是底层 Map 中的键值对,不是仅删键 - 用增强 for 循环时无法调用
remove(),所以必须显式获取Iterator
替代方案:什么时候该换 entrySet()?
如果你的循环体里**每次都要取值**,那 keySet() + get() 实际做了两次哈希定位(一次找键,一次查值),而 entrySet() 只需一次。
- 性能差异在小 Map 上几乎不可测,但在 10 万+ 条目、高频调用场景下值得切换
- 正确写法:
for (Map.Entry<String, String> entry : map.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); // ✅ 零额外哈希开销 } -
entrySet()同样不保证顺序;如需有序,请用TreeMap或先收集后排序
keySet() 就是因为逻辑上只关心“键”,顺带取值。但只要循环体里出现超过一次 map.get(key),就该立刻警觉——这已经是在为低效埋单。

















