如果我用Java创建一个2Dint
数组,然后使用Arrays.copyOf()
复制它,就像so-
jshell> int[][] c1 = {{1,2}, {3,4}}
c1 ==> int[2][] { int[2] { 1, 2 }, int[2] { 3, 4 } }
jshell> int[][] d1 = Arrays.copyOf(c1, c1.length)
d1 ==> int[2][] { int[2] { 1, 2 }, int[2] { 3, 4 } }
如果我更改了副本中的一个元素,为什么原始2D阵列中的相应单元会在这个过程中发生突变?
jshell> d1[0][0] = 0
$21 ==> 0
jshell> d1
d1 ==> int[2][] { int[2] { 0, 2 }, int[2] { 3, 4 } }
jshell> c1
c1 ==> int[2][] { int[2] { 0, 2 }, int[2] { 3, 4 } } // c1[0][0] was 1 originally
这让我相信,在使用Arrays.copyOf()
复制2D阵列的过程中,只为最外层的阵列创建一个单独的副本,而每个内部阵列仍然是原始2D阵列的内部阵列的引用?
jshell> d1 = null
d1 ==> null
jshell> c1
c1 ==> int[2][] { int[2] { 0, 2 }, int[2] { 3, 4 } }
如果是,为什么会这样?Arrays.copyOf()
不应该创建不同的副本吗?至少根据文档是这样吗?Oracle文档中是否记录了这种行为?
最后,创建2D阵列的不同副本的正确方法是什么,就像Arrays.copyOf()
用于1D阵列一样?
2D数组基本上是一个包含数组的数组,而Arrays.copyOf
执行浅层复制,因此只复制外部数组(数组的数组(,而不复制数组内部的值(在这种情况下,是int
的数组,即int[]
的数组(。因此,原始数组和副本都包含相同的int[]
数组,因此,如果通过其中一个进行修改,则通过另一个也可以看到结果。
javadoc:中明确提到了这一点
对于在原始数组和复制时,两个数组将包含相同的值。
您确实需要在了解签名<T> T[] copyOf(T[], int)
的情况下阅读。复制的数组是T[]
(T
的数组,其中T
是int[]
(,而不是T[][]
!
对于2D阵列的深度复制,您必须自己深度复制阵列,例如,请参阅如何在Java中进行2D阵列的深层复制?
方法Arrays.copyOf
不执行数组的深度复制,System.arraycopy
方法也不执行。您应该用所需的复制深度自己实现数组的深度复制的算法。例如:
int[][] arr1 = {{1, 2}, {3, 4}}; // original array
int[][] arr2 = Arrays.copyOf(arr1, arr1.length); // shallow copy
int[][] arr3 = Arrays.stream(arr1) // deep copy
.map(Arrays::stream)
.map(IntStream::toArray)
.toArray(int[][]::new);
arr1[0][0] = 7;
System.out.println(Arrays.deepToString(arr1)); // [[7, 2], [3, 4]]
System.out.println(Arrays.deepToString(arr2)); // [[7, 2], [3, 4]]
System.out.println(Arrays.deepToString(arr3)); // [[1, 2], [3, 4]]
对象数组也是如此:
public class Test {
public static void main(String[] args) {
SomeObject[] arr1 = { // original array
new SomeObject(1),
new SomeObject(2),
new SomeObject(3),
new SomeObject(4)};
SomeObject[] arr2 = Arrays.copyOf(arr1, arr1.length); // shallow copy
SomeObject[] arr3 = Arrays.stream(arr1) // deep copy
.mapToInt(SomeObject::getField)
.mapToObj(SomeObject::new)
.toArray(SomeObject[]::new);
arr1[0].setField(7);
System.out.println(Arrays.toString(arr1)); // [7, 2, 3, 4]
System.out.println(Arrays.toString(arr2)); // [7, 2, 3, 4]
System.out.println(Arrays.toString(arr3)); // [1, 2, 3, 4]
}
static class SomeObject {
int field;
public SomeObject(int field) { this.field = field; }
public int getField() { return field; }
public void setField(int field) { this.field = field; }
public String toString() { return String.valueOf(field); }
}
}