HashMap 的 containsKey 是如何判断键存在的
containskey 通过计算哈希值定位桶,并遍历链表或红黑树比对对象。它依赖 equals() 方法确认逻辑相等性,而非引用地址。

import java.util.HashMap;
public class Main {
public static void main(String[] args) {
HashMap<String, Integer> map = new HashMap<>();
map.put("key", 1);
// 使用字符串作为键
boolean exists = map.containsKey("key");
System.out.println(exists); // true
// 自定义对象作为键
KeyObj obj = new KeyObj("id1");
map.put(obj, 2);
boolean objExists = map.containsKey(new KeyObj("id1"));
System.out.println(objExists); // true
}
}
class KeyObj {
String id;
KeyObj(String id) { this.id = id; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof KeyObj)) return false;
KeyObj keyObj = (KeyObj) o;
return id.equals(keyObj.id);
}
@Override
public int hashCode() {
return id.hashCode();
}
}运行结果输出两行 true。第一个测试验证了标准字符串键的行为。第二个测试展示了当键是自定义对象时,必须重写 equals() 和 hashCode() 才能保证 containsKey 正确工作。如果未重写这两个方法,即使内容相同的两个不同实例也会被判定为不同的键。
哈希冲突时 containsKey 的处理机制
当多个键的哈希值相同时,它们会存储在同一个桶中。此时 containsKey 会逐个比较节点。在 Java 8 之前,只支持链表结构;之后,当链表长度超过阈值且元素数量足够多时,会转换为红黑树以提高查找效率。

立即学习“Java免费学习笔记(深入)”;
你是 TypeScript 项目架构专家,专注于脚手架搭建生产就绪的 Node.js 和前端应用。生成包含现代工具链(pnpm、Vite、Next.js)、类型
import java.util.HashMap;
import java.util.Map;
public class CollisionTest {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>(1);
// 强制产生哈希冲突:容量为1,初始put一个元素
map.put("A", "val1");
// 构造一个与 "A" 哈希值相同的键(模拟)
// 注意:String 的 hashCode 很难精确控制到完全相同且不改变内容
// 这里演示逻辑流程,实际开发中通常由 JVM 处理
// 检查是否存在
boolean result = map.containsKey("A");
System.out.println(result);
}
}该程序演示了基础场景。在实际底层实现中,getNode(hash, key) 方法首先计算 hash & (capacity - 1) 找到桶索引。如果该位置有节点,先检查头节点的哈希和 equals 是否匹配。如果不匹配且存在后续节点,则进入循环。如果是链表,依次调用 equals;如果是树,则按树的规则查找。这种机制确保了即使在哈希碰撞严重的情况下,也能准确找到目标键。
传入 null 键时的行为表现
HashMap 允许存储一个 null 键。当调用 containsKey(null) 时,它会直接检查桶中是否存在值为 null 的键,而不会抛出空指针异常。这是因为它对 null 键做了特殊处理,其哈希值固定为 0。

import java.util.HashMap;
public class NullKeyTest {
public static void main(String[] args) {
HashMap<String, String> map = new HashMap<>();
map.put(null, "nullValue");
// 检查 null 键是否存在
boolean hasNull = map.containsKey(null);
System.out.println(hasNull); // true
// 检查非 null 键
boolean hasNonNull = map.containsKey("other");
System.out.println(hasNonNull); // false
}
}执行上述代码,控制台将打印 true 和 false。这表明 HashMap 内部维护了一个特殊的逻辑来处理 null 键。在 put 操作中,null 键总是被放置在索引为 0 的桶的第一个位置。因此,containsKey(null) 只需检查该特定位置即可,无需进行复杂的哈希计算或遍历。这一特性使得在处理可能包含缺失值的映射时更加灵活,但也要求开发者明确区分 null 键和不存在的情况。

















