二维数组深拷贝需外层新建数组并逐行复制内层数组,因clone()和arraycopy()仅复制引用,未复制子数组对象,导致修改仍相互影响。
java 中二维数组的“牵一发而动全身”,本质是浅拷贝导致多个变量共享同一堆内存中的子数组引用。要真正隔离修改影响,必须对**每一层都做值拷贝**——即先拷贝外层数组(行引用),再逐行拷贝内层数组(列数据)。这不是简单调用一次 clone() 或 arrays.copyof() 就能解决的。
为什么 clone() 和 arraycopy() 对二维数组只是“假深拷贝”
Java 没有真正的多维数组,只有“数组的数组”。例如 int[][] a = {{1,2},{3,4}}; 中:
-
a是一个长度为 2 的引用数组,其元素a[0]和a[1]分别指向两个独立的一维数组对象; -
a.clone()只复制了外层数组的引用(即复制了a[0]和a[1]这两个地址),但没复制它们所指向的一维数组本身; -
System.arraycopy(a, 0, b, 0, a.length)同理,只复制外层数组的引用内容,子数组仍共用。
所以修改 b[0][0],等于修改了 a[0] 所指向的那个一维数组的第一个元素 —— a[0][0] 自然跟着变。
正确实现二维数组深度拷贝的三种可靠方式
核心原则:外层分配新数组 + 内层逐行复制(每行都要新建并填充)。
-
方式一:用 for 循环 + Arrays.copyOf(推荐,清晰可控)
适用于所有基本类型和引用类型二维数组:int[][] src = {{1,2,3}, {4,5}};<br>int[][] dst = new int[src.length][];<br>for (int i = 0; i dst[i] = Arrays.copyOf(src[i], src[i].length);<br>} -
方式二:用 for 循环 + System.arraycopy(高性能场景)
适合大数据量、对性能敏感的场合:int[][] dst = new int[src.length][];<br>for (int i = 0; i dst[i] = new int[src[i].length];<br> System.arraycopy(src[i], 0, dst[i], 0, src[i].length);<br>}
-
方式三:使用 Arrays.deepCopy 工具类(需自行封装)
Java 标准库没有Arrays.deepCopy(int[][]),但可封装通用方法:public static int[][] deepCopy(int[][] src) {<br> if (src == null) return null;<br> int[][] dst = new int[src.length][];<br> for (int i = 0; i if (src[i] != null) {<br> dst[i] = Arrays.copyOf(src[i], src[i].length);<br> }<br> }<br> return dst;<br>}
验证是否真为深拷贝的关键操作
不能只看两个二维数组对象的 hashcode 不同,必须测试子数组是否真正隔离:
- 修改
dst[0][0]后,检查src[0][0]值是否不变; - 进一步验证
src[0] == dst[0]应为false(说明行数组对象不同); - 用
Arrays.deepEquals(src, dst)可判断逻辑内容相等,但不反映内存独立性; - 调试时打印
System.identityHashCode(src[0])和System.identityHashCode(dst[0]),确保二者不等。
常见陷阱与规避建议
实际开发中容易忽略的细节:
-
空行处理:若某行是
null(如String[][] arr = {{"a"}, null, {"c"}}),直接Arrays.copyOf(arr[i], ...)会 NPE,需先判空; -
不规则二维数组:各行长度不同(Jagged Array)是合法的,拷贝时必须用
src[i].length而非固定值; - 引用类型元素(如 String[][]、MyObj[][]):上述方法只保证“数组结构”深拷贝,不递归拷贝元素对象本身(即 String 引用不变,但若元素是可变对象,仍需额外深拷贝其内容);
-
误用 Arrays.toString() 验证:它只输出外层数组引用信息,要用
Arrays.deepToString()看完整嵌套结构。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











