2012-03-16 3 views
0

나는 다음 시나리오를 가지고있다. 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 객체의 복제본을 만들지는 않았지만 두 변수가 동일한 객체를 가리 킵니다.

왜 이런가요? 나는 그것을 알아낼 수 없습니다. 메서드 작업을 복제하지 않는 이유는 무엇입니까?

자세한 정보가 필요하면 문의하십시오. 하지만 이것에 관해서는 두렵습니다. 정말로 당신에게 더 많은 것을 줄 수는 없지만, 아마도 시도 할 수는 있습니다.

내가 뭔가를 누락 될 수 있습니다,하지만 난 ...

감사를 어떻게 알아낼 수 없습니다.

편집

죄송합니다, 오타했다. 늦은 hre이고 나는 피로하다. 나는 그것이 :(작동하지 않는 것처럼 내가 혼란 스러워요 이유, 실제로 복제 방법을 사용하고 있습니다.

+2

Java의 다차원 배열은 배열에 대한 포인터의 배열입니다. Clone은 첫 번째 배열 만 복제합니다. –

+0

그렇다면 어떻게해야합니까? 한 배열에서 다른 배열로 원시 int를 복사하는 루프를 만들어야합니까? – AndreiBogdan

+2

내부 배열을 복제하는 루프를 만들어야합니다. 또는 Arrays 클래스에서이 작업을 수행 할 수있는 함수가있을 수 있습니다. 실제로 발명 된 이후로 클래스를 실제로 연구하지 않았습니다. –

답변

1

시도 클론 그것을 사용하는 클론() 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 : clone()의 Api는 개체의 복사본을 반환하지만 개체가 복제 된 개체에 따라 동작이 다를 수 있다고합니다. 대안으로 배열을 반복 해보십시오. 사실, 배열은 개체 또는 원시 데이터 유형을 향한 더 값 만 포인터가없는

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

옛는 "원래 배열"이고 사본은 사본

+0

죄송합니다, typo .... pls 지금 편집을 확인하십시오. 나는 당신이 말한 것처럼 했어 ... 그냥 잘못 쓰셨 : 피곤한 * 하품 * – AndreiBogdan

+0

코드를 편집 한 것을 잊었습니다. 이걸 써야만한다. –

+0

네 ... 그걸 알았어, 왜 그걸 복제하지 않을지 모르겠다. 감사. – AndreiBogdan

2

당신은 matrix1matrix1Clone 동일한 참조를주고있다. 당신이 너무 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

이다 : 그것은 2 차원 배열입니다 때문에, 당신은 중첩 루프가 필요합니다. 자세한 대답을 원한다면 여기 내 주석을 읽어야합니다 : Java is NEVER pass-by-reference, right?...right? 또는 여기 : In Java, what is a shallow copy?

그래서 배열이 포인터이기 때문에 포인터가있는 포인터를 복제하면 어떻게됩니까? 처음에는 포인터가 실제 복사되었지만 이러한 포인터는 복제되지 않은 다른 오브젝트를 가리 킵니다. 그래서 당신이 복제하고 싶다면, 나는 배열을 사용하지 않고 "더 힘든"데이터 구조를 제안한다 : 클래스. 또 다른 가능성은 배열 내에 배열을 저장하지 않을 것입니다 ... 저는 컨테이너에 대해서만 배열을 사용합니다!

하지만 배열과 (어쨌든 일부 OO 원칙을 위반하고 코드가 추악하게 보이기 때문에) 가능한 모순 때문에 내가 다룰 수 없으므로 Java 다차원 generics에 대한 세부 정보를 제공 할 수는 없습니다. .

편집

내가 복제 방법은 클래스 내에 배열 작동하는 방법을 몇 가지 테스트를 실행하고, 어떤 문제가 어느 우리가 해결 방법.

첫 번째 테스트 데이터 구조 :

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)

즐기십시오!

UPDATE : (: 당신은 서로 완전히 독립적 인 개체가해야하는 경우 일반적으로) 당신이 스레드를 처리하는 경우에만 임시 스택을 원하는 경우 객체의 복사본을 사용할 필요가 또는 참고. 그렇지 않으면 어떤 사본도 만들어서는 안됩니다! 또한 파일이나 소켓에 데이터를 쓰는 경우 복사본이 필요하지 않습니다. 더군다나 나는 실제로 사용되는 경우에만 복사 방법을 구현하는 것이 좋습니다. 데이터 구조 (모델)입니다. 따라서이 강력한 방법을 사용하여 조심하십시오 (그렇지 않으면 앱 속도가 느려지거나 아무 이유없이 수백만 개의 사본을 작성하는 경우 Java VM 저장소를 채울 수 있습니다. 실제로 스택 오버 플로우가 발생합니다).

편집

나는 조금 더이 문제에 대한 작업을했다. 갑자기 알았 기 때문에 Java API에없는 "원시"배열의 public clone() 메서드가 있습니다! (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; 
    } 
} 

을 그리고 지금 우리가 바로 상자에서이 결과를 얻을 :), I는 다음과 같이() 위 클래스의 복제 방법을 변경할 수 있습니다 해결할

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))는 false가됩니다!

솔루션 : 클래스의 복제 방법에서 모든 클래스 변수를 복제해야합니다. (그러나 일부 클래스 변수가 ArrayLists 또는 그 이상의 차원 배열 인 경우이 솔루션조차 작동하지 않습니다!)

마지막으로 실제 문제는 무엇입니까? ArrayList 클래스는 배열을 복제하지 않고 Array 클래스의 copyOf 메서드 만 호출하므로 유해합니다. ArrayList 클래스의 복제 메서드를 절대로 사용하지 마십시오. 복제 메서드가 작동하지 않으므로 ArrayList의 클래스를 상속하지 않습니다! (ArrayList 클래스가 프리미티브와 객체 만 포함하는 경우에만 작동합니다. 그렇지 않으면 위의 쉬운 ByteArray 솔루션을 사용하십시오!).

Object [] []와 같은 더 많은 차원의 배열을 사용하면 항상 위의 ByteArray 솔루션을 구현해야하므로 복제 할 수 없습니다. 배열이 거대한 경우에는 시간이 걸리고 일부 RAM이 필요할 수도 있습니다.

이제 복제 전문가입니다! :-D