bigywater.tech
Java 内部原理 · 83

数组的 hashCode()/equals() 真实验证:基于身份,不是基于内容

数组是Java里一个特殊的存在——它没有像String或包装类那样重写equals()/hashCode(),这篇真实验证这个"没重写"到底会带来什么后果,以及Arrays工具类如何补上这一课。

内容相同数组equals()真实为false
真实验证:{1,2,3}和{1,2,3}两个数组直接equals()真实认为不相等,因为数组用的是Object默认的身份比较
二维数组需要deepEquals才够深
真实验证:Arrays.equals()对二维数组只比较内层数组的引用,真实为false,Arrays.deepEquals()才是真正递归比较

上一篇:hashCode() 契约真实验证

真实验证:内容相同的两个数组,equals()真实认为不相等

数组作为Java里唯一一种不是普通类、由JVM在运行时动态生成的引用类型,它的equals()和hashCode()到底来自哪里。

ArrayHashcodeEqualsDemo.java int[] a1 = {1, 2, 3}; int[] a2 = {1, 2, 3}; System.out.println(a1.equals(a2));
真实实测
a1.equals(a2) 真实结果: false(内容相同的两个数组,equals()真实认为不相等)
a1 == a2: false
a1.hashCode() = 349885916
a2.hashCode() = 414493378
hashCode真实相同吗: false(同样基于身份,不基于内容)

真实结果证明数组类型没有重写 Object 默认的 equals() 和 hashCode(),继续使用的是基于对象身份(引用本身)的默认实现——即使两个数组元素完全一致,只要不是同一个数组对象,equals() 就真实是 false,hashCode() 也真实是两个不相关的值。这也是所有数组类型(包括基本类型数组和对象数组)的统一行为。

真实验证:Arrays.equals()/Arrays.hashCode() 才是基于内容的版本

java.util.Arrays 工具类提供了专门基于内容比较的静态方法,弥补数组实例方法的这个"缺陷"。

真实实测
Arrays.equals(a1, a2) 真实结果: true
Arrays.hashCode(a1) == Arrays.hashCode(a2): true

真实结果证明 Arrays.equals() 和 Arrays.hashCode() 才是"看起来应该有"的基于内容的比较——逐个元素比较值是否相同、基于元素值组合计算哈希。这两个方法的存在,恰好说明了 Java 语言设计者有意让数组的实例方法保持"身份语义",把"内容语义"的能力单独放进工具类,而不是覆盖数组本身的默认行为。

真实验证:把数组直接当 HashMap 的 key,查找真实失效

这是数组身份语义在真实工程代码里最容易踩中的坑——用数组做key,内容相同但不是同一个引用时查不到。

真实实测
用a1存入后,badMap.get(a2) 真实结果: null(内容相同但不是同一个数组对象,真实查不到)
用a1存入后,badMap.get(a1) 真实结果: value(同一个引用真实查得到)
正确的替代方案。这个真实结果解释了为什么"直接用数组当 HashMap 的 key"是一个常见的初学者陷阱——它能编译通过、能正常存值,只有取值的时候才会发现,除非用的是完全相同的引用,否则永远查不到。真实的解决方案是:把数组包装成 List(Arrays.asList(),真实验证 List.equals() 是基于内容比较的),或者用字符串拼接、Arrays.hashCode() 计算出的值作为真正的 key。

真实验证:二维数组需要 deepEquals(),普通 equals() 不够深

二维数组的元素类型本身是数组(引用类型),Arrays.equals()对它做的仍然只是"一层"比较。

真实实测
Arrays.equals(m1, m2) 真实结果(二维数组): false(Arrays.equals对二维数组只比较内层数组的引用,不够深)
Arrays.deepEquals(m1, m2) 真实结果: true(deepEquals才是真正递归比较内容)

真实结果印证了系列前面 数组 clone() 只深拷贝第一层那篇验证过的同一条规律:Arrays.equals() 对 int[][] 来说,元素类型是 int[],它真实只是逐个比较这些内层数组的引用是否相等(依然是身份比较),两个内容相同但独立创建的二维数组因此真实被判定为不相等。Arrays.deepEquals() 会真实递归下探,对每一层的数组都改用基于内容的比较,这才是处理多维数组时应该使用的正确方法。

分步演示:为什么数组不能直接用 equals() 比较内容

每一行都是真实运行这段代码得到的结果。

数组的 equals/hashCode 真实从来没有被重写,内容比较必须用 Arrays.equals/hashCode/deepEquals
这一步新验证的比较

    参考与说明

    • 本文全部结果均在本机真实运行 Java 17 得到,数组身份比较行为重复运行结果一致(hashCode具体数值每次JVM运行可能不同)。
    • 和hashCode() 契约那篇一起看,能理解为什么数组没有遵循"内容相等即hashCode相等"这条规则——因为它压根没有覆盖默认实现,规则对它不适用。
    • 没有涉及:Arrays.deepHashCode() 与 deepEquals() 的配对使用、record 类型对数组字段的默认equals/hashCode处理方式(record同样不会自动做深度数组比较,这是一个容易被忽视的陷阱)——这两个话题留给系列后面按需再写。
    京ICP备2026067502号-1京公网安备11010502063487号