2012-03-16 67 views
0

我的下一個情景:錯誤使用Object.clone()

我定義我的主類的int[][]變量。 int[][] matrix1 = new int[10][10]我給它一些價值。然後我調用一個方法,並將該變量作爲參數發送給該方法。作爲它發送的對象不是通過引用而是通過引用,因此在該方法內部,因爲我必須更改由matrix1包含的值,但在從方法返回後不會影響對象,因此我將它複製爲如下形式:

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

但問題是,我對matrix1Clone所做的更改也發生在matrix1中。所以它並沒有真正創建一個matrix1對象的克隆,但是兩個變量指向同一個對象。

這是爲什麼?我似乎無法弄清楚。爲什麼不克隆方法的工作?

如果您需要更多信息,請詢問。但我擔心這是關於它,不能給你更多,但也許我可以嘗試。

我可能會錯過一些東西,但我無法弄清楚什麼...

謝謝。

EDIT

對不起,做了一個拼寫錯誤。它晚了,我累了。我使用clone方法確實,這就是爲什麼我很困惑,因爲它不工作:(。

+2

Java中的多維數組是指向數組的指針數組。克隆只會克隆第一個數組。 – 2012-03-16 12:33:16

+0

那麼我該如何做到這一點?我是否需要製作一個將原始int從一個數組複製到另一個數組的循環? – AndreiBogdan 2012-03-16 12:33:51

+2

您需要製作一個克隆內部陣列的循環。或者在Arrays類中可能會有這樣一個功能 - 自從它發明以來,我沒有真正研究這個類。 – 2012-03-16 15:19:38

回答

1

嘗試克隆使用它的clone()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(); 
} 

或使用循環複製所有值

編輯:Api for clone()表示它應該返回對象的副本,但行爲可能會因所克隆的對象的不同而有所不同。嘗試迭代數組作爲替代。因爲它是一個二維數組,你需要一個嵌套的循環:

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

old爲「原始陣列」及副本複印件

+0

對不起,犯了一個錯字....請檢查後編輯。我這樣做就像你說的...只是寫錯了:)厭倦*打哈欠* – AndreiBogdan 2012-03-16 12:32:17

+0

忘了提及我編輯的代碼。應該使用這個工作 – 2012-03-16 12:45:43

+0

是啊......我知道,這只是我不知道爲什麼它不會克隆它。謝謝。 – AndreiBogdan 2012-03-16 12:48:23

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

抱歉,發了一個錯字....請在編輯後檢查。我這樣做就像你們說的......只是我累了,我犯了一個錯字。對不起 – AndreiBogdan 2012-03-16 12:32:42

+0

編輯我的答案 – evilone 2012-03-16 12:41:58

+0

偉大....一個更多的方法...就好像我有足夠的:) ..謝謝你。我會在一分鐘內看一遍。 – AndreiBogdan 2012-03-16 12:46:38

0

其實,陣列具有對對象或原始數據類型沒有值,但指針。如果你想要一個詳細的答案,你應該在這裏閱讀我的評論:Java is NEVER pass-by-reference, right?...right?或這裏:In Java, what is a shallow copy?

因此,作爲數組是指針,如果你克隆一個指針指針,它會發生什麼?首先,指針被複製爲真實的,但這些指針只指向其他未被克隆的對象。所以如果你想克隆,我建議不要使用數組,而是使用「更難」的數據結構:類。另一種可能性是永遠不會在數組中存儲數組......就像我僅爲容器使用數組一樣!

但是我不能給你關於Java多維泛型的細節,因爲我從來沒有處理過它們,不僅僅是因爲它們可能不一致,因爲它們是數組(它們違反了一些面向對象原則,使代碼看起來很醜) 。

編輯

我跑了幾個測試的克隆方法的工作原理爲數組類裏面,有什麼問題和變通方法,我們有。

首先,測試數據結構:

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 

這是不是好!!

那麼解決方法是什麼?我建議在每一個數據結構類這樣做:

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

這種解決方案的優點:

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; 
     } 
    } 
} 

所以,你將通過實施只用一行代碼得到一個完整拷貝它通常足以實現Serializable接口來創建一個「可複製」的類。如果沒有,您可以通過閱讀Serializable Javadoc中編寫的內容來解決任何問題!

甚至更​​多:無論您要製作「可複製」的課程中的什麼類型的對象,都無需在此問題上花費更多時間。畢竟,上面的代碼是迄今爲止深入嵌入Java的最簡單且最快的解決方案,並且僅使用RAM! (感謝ByteArrayOutputStream)

享受!

更新:請注意,如果您想要臨時堆棧或者如果您要處理線程(通常:如果您需要使對象完全獨立),則只需要使用對象的副本。否則,你不應該做任何複製!另外,如果您將一些數據寫入文件或套接字中,則不需要副本。甚至更多我建議只在真正使用時才實現複製方法:用於數據結構(模型)。所以使用這個強大的方法時要小心(否則它可能會減慢你的應用程序的速度,或者如果你無緣無故地製作數百萬份拷貝,甚至會填滿Java VM存儲空間,這確實會導致stackoverflow:o)。

編輯

我工作多一點就這一問題。因爲我突然發現,有一個公共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不克隆它的數組,它只調用類Array中的方法copyOf,這是有害的。 因此,切勿使用類ArrayList的克隆方法,也不要從ArrayList繼承任何類,因爲它的克隆方法不起作用!(它只有在類ArrayList只包含原語並且沒有對象的情況下才起作用,否則只需使用上面簡單的ByteArray解決方案!)。

請注意,對於像Object [] []這樣的更多維數組,您總是需要實現上面的ByteArray解決方案,它們不能被克隆!如果你的陣列很大,可能需要一段時間,並且需要一些RAM。

現在你是克隆專家! :-D