2012-03-16 5 views
0

У меня есть следующий сценарий:Ошибки при использовании Object.clone()

Я определяю int[][] переменные в моем главном классе. int[][] matrix1 = new int[10][10], и я даю ему некоторые значения. Затем я вызываю метод, и я отправляю эту переменную в качестве параметра к этому методу. Будучи объектом, который он отправляет, ссылается не по значению, поэтому внутри метода, потому что мне нужно изменить значения, содержащиеся в матрице1, но не влиять на объект после его возвращения из метода, я делаю его клон следующим образом:

private void myMethod(int[][] matrix1) 
{ 
    int[][] matrix1Clone = matrix1.clone(); 
    //And next i do some changes to matrix1Clone 
    ...... 
} 

Но проблема в том, что изменения, которые я делаю для matrix1Clone, также происходят в матрице1. Поэтому он не создал клон объекта matrix1, но обе переменные указывают на один и тот же объект.

Почему это? Кажется, я не понимаю. Почему метод клонирования не работает?

Если вам нужна дополнительная информация, задайте вопросы. Но я боюсь, что это так, не может дать вам больше, но, возможно, я мог бы попробовать.

я мог бы что-то не хватает, но я не могу понять, что ...

Спасибо.

EDIT

К сожалению, сделал опечатку. Уже поздно, и я устал. Я использую метод клонирования на самом деле, именно поэтому я запутался, как это не работает :(.

+2

Многомерный массив в Java представляет собой массив указателей на массивы. Клон будет клонировать только первый массив. –

+0

Итак, как я могу это сделать? Нужно ли создавать цикл, который копирует примитивный int из одного массива в другой? – AndreiBogdan

+2

Вам нужно сделать цикл, который клонирует внутренние массивы. Или в классе Arrays может быть функция, чтобы сделать это - я не изучал этот класс с тех пор, как он был изобретен. –

ответ

1

Try клон его, используя клон() http://docs.oracle.com/javase/1.4.2/docs/api/java/lang/Object.html#clone%28%29

private void myMethod(int[][] matrix1) 
{ 
    int[][] matrix1Clone = matrix1.clone(); 
} 

или, скопируйте все значения с помощью цикла

EDIT: Api для clone() говорит, что он должен возвращать копию объекта, но поведение может отличаться в зависимости от того, какой объект клонируется. Попробуйте выполнить итерацию по массиву в качестве альтернативы. Так как это 2d массив, вам нужен вложенный цикл:

for(int i=0; i<old.length; i++) 
    for(int j=0; j<old[i].length; j++) 
    old[i][j]=copy[i][j]; 

где давность «исходный массив» и копия является копией

+0

извините, сделали опечатку .... PLS сейчас проверить после редактирования. Я сделал это, как вы сказали ... просто написал это неправильно :) Усталость * зевать * – AndreiBogdan

+0

Забыл упомянуть, что я редактировал код. Должен работать с этим –

+0

Да ... я знал это, это просто я не был уверен, почему, черт возьми, он не будет клонировать его. Благодарю. – AndreiBogdan

2

Вы даете matrix1Clone ту же ссылку, как matrix1. Если изменить matrix1Clone то matrix1 изменения тоже.

Вы можете скопировать ваш массив с Перебор массива источников:

public static int[][] clone2DArray(int[][] array) { 
     int rows = array.length; 

     //clone the 'shallow' structure of array 
     int[][] newArray = array.clone(); 
     //clone the 'deep' structure of array 
     for(int row = 0; row < rows; row++){ 
      newArray[row] = array[row].clone(); 
     } 

     return newArray; 
    } 
+0

извините, сделал опечатку .... PLS проверить сейчас после редактирования. Я сделал это, как вы, ребята, сказал ... просто я устал, и я сделал опечатку. Извините – AndreiBogdan

+0

отредактировал мой ответ – evilone

+0

Отличный .... еще один метод ... как будто у меня было достаточно :) .. Благодарим вас. Я посмотрю через минуту. – AndreiBogdan

0

На самом деле, массивы не имеют значения, но указатели по отношению к объекту или примитивных типов данных. Если вы хотите получить подробный ответ, вы должны прочитать мой комментарий здесь: Java is NEVER pass-by-reference, right?...right? или здесь: In Java, what is a shallow copy?

Итак, как массивы являются указателями, что произойдет, если вы клонируете указатель с указателями в нем? Сначала указатели копируются для реального, но эти указатели указывают только на другой объект, который не клонирован. Поэтому, если вы хотите клонировать, я предлагаю не использовать массивы, а «более сложные» структуры данных: классы. Другая возможность никогда не будет хранить массив внутри массива ... например, я использую массивы только для контейнеров!

Но я не могу дать вам подробные сведения о многомерных дженериках Java, поскольку я никогда не занимаюсь ими не только из-за их возможной несогласованности, потому что они являются массивами (они все равно нарушают некоторые принципы OO и делают код уродливым) ,

EDIT

я бегу несколько тестов как метод клон работает для массивов внутри класса, в чем проблема, и какие обходные пути у нас есть.

Первая структура тестовых данных:

public class Foobar implements Cloneable { 
    String[] array; 

    public Foobar() { 
     this.array = new String[10]; 
    } 

    public String getValue(){ 
     return array[0]; 
    } 

    public String[] getArray(){ 
     return array; 
    } 

    public void setArray(String[] array){ 
     this.array = array; 
    } 

    @Override 
    public Object clone(){ 
     try{ 
      Foobar foobar = (Foobar) super.clone(); 
      foobar.setArray(array); 
      return foobar; 
     } 
     catch(Exception e){ 
      return null; 
     } 
    } 
} 

Теперь контроллер:

String[] array = new String[10]; 
array[0] = "111"; 
Foobar foo1 = new Foobar(); 
foo1.setArray(array); 
Foobar foo2 = foo1; //Alternation: Foobar foo2 = (Foobar) foo1.clone(); 
System.out.println("Instance: "+foo1.getArray()+" with value: "+foo1.getValue()); 
System.out.println("Instance: "+foo2.getArray()+" with value: "+foo2.getValue()); 
array[0] = "999"; 
System.out.println("Instance: "+foo1.getArray()+" with value: "+foo1.getValue()); 
System.out.println("Instance: "+foo2.getArray()+" with value: "+foo2.getValue()); 

Результаты испытаний всегда будет выглядеть - независимо от того, если я использую = или клон():

Instance: [Ljava.lang.String;@42e816 with value: 111 
Instance: [Ljava.lang.String;@42e816 with value: 111 
Instance: [Ljava.lang.String;@42e816 with value: 999 
Instance: [Ljava.lang.String;@42e816 with value: 999 

Это не хорошее !!

Итак, каково обходное решение? Я предлагаю делать это в каждом классе структуры данных:

public class Foobar implements Serializable { 
    //any class variables...it doesn't matter which! 

    public Foobar() { 
     //do initialisation here...it doesn't matter what you do! 
    } 

    public Foobar copy(){ 
     try{ 
      ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
      ObjectOutputStream oos = new ObjectOutputStream(baos); 
      oos.writeObject(this); 
      ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); 
      ObjectInputStream ois = new ObjectInputStream(bais); 
      Foobar foobar = (Foobar) ois.readObject(); 
      return foobar; 
     } 
     catch(Exception e){ 
      return null; 
     } 
    } 
} 

Таким образом, вы получите полную копию, реализовав только одна строка кода:

Foobar foo2 = foo1.copy(); //nice and easy!! 

Преимущество этого решения: Это, как правило, достаточно реализовать интерфейс Serializable, чтобы сделать класс «копируемым». А если нет, вы можете решить любые проблемы, прочитав то, что написано в Serializable Javadoc!

Более того: не важно, какие объекты находятся в классе, который вы хотите сделать «копируемым», поэтому вам не нужно тратить больше времени на эту проблему. В конце концов, выше код является самым простым и быстрым решением, глубоко встроенным в Java с тех пор и использует только ОЗУ! (благодаря ByteArrayOutputStream)

Наслаждайтесь!

ОБНОВЛЕНИЕ: Обратите внимание, что вам нужно всего лишь использовать копию объекта, если требуется временный стек или если вы имеете дело с потоками (в общем случае, если вам нужны объекты, полностью независимые друг от друга). В противном случае вы не должны делать копии вообще! Также, если вы записываете некоторые данные в файл или сокет, вам не нужна копия. Более того, я предлагаю реализовать метод копирования только тогда, когда он действительно используется: для структур данных (модели). Поэтому будьте осторожны, используя этот мощный метод (иначе это может замедлить ваше приложение или даже заполнить хранилище Java VM, если вы делаете миллионы копий без каких-либо оснований, это может привести к тому, что stackoverflow действительно: o).

EDIT

я работал немного больше по этому вопросу. Поскольку я вдруг обнаружил, что существует общедоступный метод clone() «примитивных» массивов, которых нет в Java API! («пасхальное яйцо» из SUN для массивов типа String [] или int [] ;-)

И поскольку я использую реальные массивы в качестве основной структуры данных Foobar (а не ArrayLists!), Я могу изменить метод клонирования (из указанного класса), как это:

@Override 
public Object clone(){ 
    try{ 
     Foobar foobar = (Foobar) super.clone(); 
     String[] arrayClone = array.clone(); //who thought that this is possible?! 
     foobar.setArray(arrayClone); 
     return foobar; 
    } 
    catch(Exception e){ 
     return null; 
    } 
} 

И теперь мы получаем этот результат прямо из коробки:

Instance: [Ljava.lang.String;@42e816 with value: 111 
Instance: [Ljava.lang.String;@9304b1 with value: 111 
Instance: [Ljava.lang.String;@42e816 with value: 999 
Instance: [Ljava.lang.String;@9304b1 with value: 111 

Проблема решена с «дважды вложенными " объектов !!! Как вы можете видеть, клоны имеют разные объекты независимо от оригинала ... поэтому foo1.equals (foo2)) будет ложным!

В методе клонирования класса необходимо также клонировать все его переменные класса! (Но если некоторые переменные класса являются массивами ArrayLists или более объемными массивами, даже это решение не будет работать!)

И, наконец, в чем проблема? Класс ArrayList не клонирует его массивы, он вызывает только метод copyOf в классе Array, что является вредным. Так никогда не используйте метод clone класса ArrayList и никогда не наследуйте какой-либо класс из ArrayList, потому что его метод clone не будет работать! (Он работает только в том случае, если класс ArrayList содержит только примитивы и никаких объектов ... в противном случае используйте простое решение ByteArray выше!).

Обратите внимание, что при использовании массивов большего размера, таких как Object [] [], вам всегда нужно реализовать решение ByteArray выше, они не могут быть клонированы! И если ваш массив огромен, это может занять некоторое время и понадобиться оперативная память.

Теперь вы специалист по клонированию! :-D