HashMap 使用数组作为键时无法正确识别相同内容的键

java 中 `hashmap` 默认使用对象的 `equals()` 和 `hashcode()` 判断键是否相等;而 `int[]` 是引用类型,其 `equals()` 比较的是内存地址而非元素内容,因此即使两个 `int[]` 内容完全相同,`hashmap` 也视为不同键,导致 `get()` 返回 `null`。

根本原因在于:Java 数组没有重写 Object.equals() 方法。默认继承自 Object 的 equals() 实现仅做引用比较(即 ==),不进行内容比对。这意味着:

int[] a = {1, 2, 3};
int[] b = {1, 2, 3};
System.out.println(a.equals(b)); // false —— 即使内容相同,也是不同对象
System.out.println(Arrays.equals(a, b)); // true —— 正确的内容比较方式

同理,hashCode() 也未重写,a.hashCode() 与 b.hashCode() 极大概率不同(取决于内存地址),这进一步破坏了 HashMap 的哈希桶定位逻辑——即使 equals() 偶然返回 true,哈希冲突或桶错位仍会导致查找失败。

✅ 正确解决方案

方案 1:改用不可变、内容可比的键类型(推荐)

避免使用数组作键,改用 List 或自定义封装类:

// 使用 List(自动支持内容 equals/hashCode)
Map, int[]> coloursMap = new HashMap<>();
List coord = Arrays.asList(i, j, k);
coloursMap.put(coord, new int[]{1, 2, 3});

// 查找时同样构造相同 List
public int[] getColourFromHashInts(int i, int j, int k) {
    return coloursMap.get(Arrays.asList(i, j, k));
}
✅ 优势:简洁、线程安全(若用 Collections.unmodifiableList)、语义清晰。

方案 2:使用 Arrays.hashCode() + Arrays.equals() 自定义包装类

若必须用原始数组,可封装为 Key 类:

public static final class CoordKey {
    private final int[] coords;

    public CoordKey(int i, int j, int k) {
        this.coords = new int[]{i, j, k};
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
   

if (o == null || getClass() != o.getClass()) return false; CoordKey coordKey = (CoordKey) o; return Arrays.equals(coords, coordKey.coords); } @Override public int hashCode() { return Arrays.hashCode(coords); } } // 使用: Map map = new HashMap<>(); map.put(new CoordKey(i, j, k), cols);

❌ 错误尝试(不推荐)

  • Arrays.asList(new int[]{i,j,k}) → 返回 List(单元素列表),非 List,无效;
  • new Integer[]{i,j,k} → 虽可比,但装箱开销大,且 Integer[] 同样不重写 equals()(需用 Arrays.equals)。

⚠️ 注意事项

  • HashMap 键对象在插入后不应修改其影响 hashCode()/equals() 的状态(对数组而言即不能修改元素值);
  • 若键需序列化或跨线程共享,优先选择不可变类型(如 List.of(i,j,k) Java 9+);
  • 性能敏感场景下,可预计算哈希码(如 i * 31^2 + j * 31 + k)实现轻量 CoordKey。

总之,永远不要直接用原始数组(int[], String[] 等)作为 HashMap 的键——这是 Java 集合使用的经典陷阱之一。