1. 概述

本文深入解析 Java 中 Arrays 类的 deepEquals 方法。我们将探讨它的适用场景,并通过几个典型示例帮助你彻底掌握其使用方式。

如果你还想了解 java.util.Arrays 类中其他常用方法,推荐阅读我们的 Java Arrays 快速指南

2. 使用目的

当你需要比较嵌套数组或多维数组是否相等时,deepEquals 是首选方法
此外,如果数组中包含自定义对象(如 Person),你还必须重写该类的 equals 方法,否则比较结果可能不符合预期。

⚠️ 注意:deepEquals 的“深比较”特性决定了它会递归遍历所有层级元素,因此性能开销比普通 equals 大,需谨慎在高频场景使用。

2.1. 方法签名

先来看一下核心方法签名:

public static boolean deepEquals(Object[] a1, Object[] a2)

关键点:

  • 不支持基本类型一维数组直接比较(如 int[]double[] 等)。
  • ✅ 若需比较基本类型数组,应使用对应的包装类(如 Integer[])或将数组转为对象数组。
  • ✅ 更推荐的做法是:对基本类型的一维数组使用 Arrays.equals(),它提供了针对各原始类型的重载方法。

2.2. 内部实现机制

deepEquals 的核心优势在于递归深度比较

  • 它不仅比较数组顶层元素,还会逐层深入子数组或嵌套结构。
  • 对每个子元素,若仍是数组,则继续调用 deepEquals 进行递归比较。
  • 直到遇到非数组对象,才调用其 equals() 方法判断。

⚠️ 踩坑提醒:
避免用于存在自引用(self-reference)的数组结构,否则会触发无限递归,最终抛出 java.lang.StackOverflowError

3. 返回结果规则

Arrays.deepEquals 的返回逻辑如下:

条件 返回值
两个数组引用相同(a1 == a2 true
两个数组都为 null true
只有一个为 null false
数组长度不同 false
两个数组都为空(长度为0) true
所有对应位置的子元素“深度相等” true
其他不匹配情况 false

“深度相等”指:递归比较每一层元素,直到基本类型或已正确实现 equals 的对象。

4. 实战示例

下面通过几个典型例子,直观展示 deepEqualsArrays.equals 的差异。

4.1. 一维对象数组

先看一个简单的 Object[] 比较:

Object[] anArray = new Object[] { "string1", "string2", "string3" };
Object[] anotherArray = new Object[] { "string1", "string2", "string3" };

assertTrue(Arrays.equals(anArray, anotherArray));
assertTrue(Arrays.deepEquals(anArray, anotherArray));

结果:两个方法都返回 true

再测试包含 null 的情况:

Object[] anArray = new Object[] { "string1", null, "string3" };
Object[] anotherArray = new Object[] { "string1", null, "string3" };

assertTrue(Arrays.equals(anArray, anotherArray));
assertTrue(Arrays.deepEquals(anArray, anotherArray));

✅ 结论:deepEquals 支持任意层级的 null 值比较,不会报错。

接下来是重点:嵌套数组场景

Object[] anArray = new Object[] { 
    "string1", 
    null, 
    new String[] {"nestedString1", "nestedString2"} 
};
Object[] anotherArray = new Object[] { 
    "string1", 
    null, 
    new String[] {"nestedString1", "nestedString2"} 
};

assertFalse(Arrays.equals(anArray, anotherArray));  // ❌ 引用不同
assertTrue(Arrays.deepEquals(anArray, anotherArray)); // ✅ 内容相同

📌 原因分析:

  • Arrays.equals 只比较顶层元素的引用,而两个子数组是不同对象,引用不等 → 返回 false
  • deepEquals 发现子元素是数组,会递归调用自身比较内容 → 返回 true

4.2. 多维基本类型数组

这是最容易踩坑的场景之一。看下面这个二维 int 数组:

int[][] anArray = { { 1, 2, 3 }, { 4, 5, 6, 9 }, { 7 } };
int[][] anotherArray = { { 1, 2, 3 }, { 4, 5, 6, 9 }, { 7 } };

assertFalse(Arrays.equals(anArray, anotherArray));   // ❌
assertTrue(Arrays.deepEquals(anArray, anotherArray)); // ✅

⚠️ 关键点:

  • Arrays.equals(int[][], int[][]) 实际上调用的是 Object.equals(),即比较数组引用。
  • 虽然内容相同,但两个二维数组是独立创建的对象 → 引用不同 → equals 返回 false
  • deepEquals 则会逐行、逐元素比较 int 值 → 内容一致 → 返回 true

✅ 正确做法:
多维基本类型数组必须用 deepEquals 才能实现内容比较。

4.3. 多维自定义对象数组

现在我们测试包含自定义类的多维数组。

先定义 Person 类,并重写 equals 方法

class Person {
    private int id;
    private String name;
    private int age;

    public Person(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null) return false;
        if (!(obj instanceof Person)) return false;
        Person person = (Person) obj;
        return id == person.id 
            && name.equals(person.name) 
            && age == person.age;
    }

    // getter/setter 省略
}

⚠️ 提醒:重写 equals 时务必同时重写 hashCode,避免违反 Java 的 equals-hashCode 合约。虽然本例未体现,但生产环境必须遵守。

接下来创建两个内容相同的二维 Person 数组:

Person personArray1[][] = { 
    { new Person(1, "John", 22), new Person(2, "Mike", 23) },
    { new Person(3, "Steve", 27), new Person(4, "Gary", 28) } 
};
Person personArray2[][] = { 
    { new Person(1, "John", 22), new Person(2, "Mike", 23) }, 
    { new Person(3, "Steve", 27), new Person(4, "Gary", 28) } 
};
        
assertFalse(Arrays.equals(personArray1, personArray2));  // ❌
assertTrue(Arrays.deepEquals(personArray1, personArray2)); // ✅

结果再次验证:deepEquals 能穿透多层结构,调用 Person.equals() 完成内容比对。

小技巧:Objects.deepEquals

还有一个更通用的方法:

assertTrue(Objects.deepEquals(personArray1, personArray2));

Objects.deepEquals(Object a, Object b) 在传入两个数组时,内部会自动委托给 Arrays.deepEquals,适合泛型或不确定类型时使用。

5. 总结

📌 核心结论:

  • 多维数组 or 嵌套数组的内容比较 → 用 Arrays.deepEquals
  • ❌ 一维基本类型数组 → 用 Arrays.equals
  • ❌ 一维对象数组(无嵌套)→ Arrays.equals 足够
  • ⚠️ 自定义对象必须重写 equals(和 hashCode
  • ⚠️ 避免用于存在循环引用的数组,防止栈溢出

本文完整示例代码已托管至 GitHub:https://github.com/java-tutorials/core-java-arrays-operations-advanced


原始标题:Arrays.deepEquals