Как клонировать многомерный массив в java?
Изменить 2: Ниже приведен фрагмент кода на основе ответа DuffyMo, который иллюстрирует, как обойти ограничения клонирования для многомерных массивов с использованием System.arraycopy.
import java.util.Arrays;
public class Randar {
public static int[][] arrayMaster = {{6,1}, {10,1}, {1,1}};
private static int[][] arrayChanges = new int[arrayMaster.length][2];
public Randar () {
}
public static void main(String[] args) {
arrayChanges[0][0] = 0;
resetArrays(arrayChanges, arrayMaster);
arrayChanges[0][0] = 0;
System.out.format("arrayMaster: %s, arrayChanges: %s", Arrays.deepToString(arrayMaster), Arrays.deepToString(arrayChanges));
}
public static void resetArrays(int[][] arrayChanges, int[][] arrayMaster) {
for (int a=0; a< arrayMaster.length; a++) {
System.arraycopy(arrayMaster[a], 0, arrayChanges[a], 0, arrayMaster[a].length);
}
// arrayChanges = arrayMaster.clone(); will NOT work as expected
}
}
[ОРИГИНАЛЬНЫЙ ВОПРОС]
Какой простой способ (полностью) клонировать многомерный массив в java? Эта программа иллюстрирует мою проблему.
import java.util.Arrays;
public class Randar {
public static int[][] arrayMaster = {{6,1}, {10,1}, {1,1}};
static private int[][] arrayChanges = arrayMaster;
public static void main(String[] args) {
arrayChanges[0][0] = 0;
resetArrays();
System.out.format("arrayMaster: %s, arrayChanges: %s",Arrays.deepToString(arrayMaster), Arrays.deepToString(arrayChanges));
}
public static void resetArrays() {
arrayChanges = arrayMaster.clone();
}
}
При запуске вышеуказанного кода arrayMaster изменяется, а также arrayChanges, в отличие от моих намерений. Думая, что я могу клонировать каждый отдельный элемент массива arrayMaster, я попытался обойти проблему с этим:
for (int iter = 0; iter < arrayMaster.length; iter++) {
arrayChanges[iter] = arrayMaster[iter].clone();
}
но когда я запускаю код, который по какой-то причине дает исключение NullPointerException. Является ли запись метода, который меняет только отдельные значения массивов в моем массиве?
Спасибо.
РЕДАКТИРОВАТЬ 1: Это также не устраняет проблему.
import java.util.Arrays;
public class Randar {
public int[][] arrayMaster = {{6,1}, {10,1}, {1,1}};
private int[][] arrayChanges = arrayMaster.clone();
public Randar () {
}
public static void main(String[] args) {
Randar Randar1 = new Randar();
Randar1.arrayChanges[0][0] = 0;
resetArrays(Randar1.arrayChanges, Randar1.arrayMaster);
Randar1.arrayChanges[0][0] = 0;
System.out.format("arrayMaster: %s, arrayChanges: %s", Arrays.deepToString(Randar1.arrayMaster), Arrays.deepToString(Randar1.arrayChanges));
}
public static void resetArrays(int[][] arrayChanges, int[][] arrayMaster) {
/*for (int a=0; a< arrayMaster.length; a++) {
System.arraycopy(arrayMaster[a].clone(), 0, arrayChanges[a], 0, arrayMaster[a].length);
} */
arrayChanges = arrayMaster.clone();
}
}
Ответы
Ответ 1
При запуске вышеуказанного кода arrayMaster изменяется, а также arrayChanges, в отличие от моих намерений.
Линия
static private int[][] arrayChanges = arrayMaster;
является виновником. В этой строке arrayChanges
и arrayMaster
указывают на один и тот же объект, поэтому при доступе к объекту из одного и того же объекта видится одно или другое.
EDIT: что происходит, когда вы клонируете одно измерение многомерного массива
Как объясняет Эрик Липперт, массив концептуально представляет собой список переменных. Если вы просто назначили другую переменную, указывающую на тот же массив a la static private int[][] arrayChanges = arrayMaster;
, вы вообще не изменили набор переменных. Вы не создали никаких новых переменных, кроме arrayChanges
, поэтому вы не получили больше памяти из операционной системы /JVM, поэтому любое изменение, которое вы делаете на arrayMaster
, применяется к arrayChanges
и наоборот.
Теперь посмотрим на двумерный массив. В Java двумерный массив представляет собой список переменных, которые имеют свойство, которое каждая из этих переменных относится к одномерному массиву. Таким образом, всякий раз, когда вы клонируете двумерный массив, вы создаете новый список переменных, каждый из которых указывает на то же место, в котором указывали старые переменные. Итак, вы немного набрали, что можете спокойно писать arrayChanges[0] = new int[10]
, не затрагивая arrayMaster
, но как только вы начнете ссылаться на arrayChanges[i][j]
, вы все равно ссылаетесь на те же массивы второго уровня, что и ссылки arrayMaster
. То, что вы действительно хотите для глубокого копирования двумерного массива ints, это
public static int[][] deepCopyIntMatrix(int[][] input) {
if (input == null)
return null;
int[][] result = new int[input.length][];
for (int r = 0; r < input.length; r++) {
result[r] = input[r].clone();
}
return result;
}
Тем, кто может взглянуть на этот ответ в будущем: да, лучше заменить int
на T
здесь и сделать метод общим, но для этого более конкретный метод глубокой копии проще объяснить хорошо.
Ответ 2
clone
выполняет "мелкую" копию. То есть внешний массив дублируется, но сохраненные в нем значения не изменяются. Поэтому, если у вас есть A1 = {B1, B2, B3} и клонировать то, что в A2, исходное содержимое A2 будет {B1, B2, B3}. Если вы измените A1 на {C1, B2, B3}, то A2 останется неизменным, но если вы измените содержимое B1 (без его замены), то A2 "увидит" это изменение.
Чтобы выполнить то, что вы хотите, вы должны пройти через внешний массив и clone
элементы этого внешнего массива (которые являются внутренними массивами).
Pidgin Java:
int[][] A2 = A1.clone();
for (int i = 0; i < A2.length; i++) {
A2[i] = A2[i].clone();
}