배열의 복사

김민성·2022년 9월 19일
0

Java

목록 보기
45/47
post-thumbnail

배열의 복사

Java에서의 배열 복사는 얕은복사와 깊은 복사가 있다.

  • 얕은 복사 : 원래 배열의 주소값을 가져온다. 따라서 복사한 배열 수정시 원본배열도 수정된다.
  • 깊은 복사 : 원래 배열을 그대로 가져와 새로운 배열을 만들어낸다. 다른 주소를 참조하므로 수정에 대해 서로 독립적이다.

1차원 배열의 복사

  • 얕은 복사 : 1차원 배열에서의 얕은 복사는 단순 배열 변수의 선언으로 이루어진다.

    int[] arrA = {1, 2, 3};
    int[] arrB = arrA;
    arrB[2] = 5;
    
    System.out.println(arrA[2]);    // 5
    System.out.println(arrA);       // [I@c818063
    System.out.println(arrB);       // [I@c818063

    분명 arrB[2] 를 변경했는데 arrA[2]도 변경된 값으로 출력되는 것을 확인할 수 있다.

    또한 arrAarrB를 각각 출력해보면 같은 주소값을 참조하고 있음을 알 수 있다.


  • 깊은 복사 : clone() 메서드를 사용해 깊은복사를 할 수 있다.

    int[] arrA = {1, 2, 3};
    int[] arrB = arrA.clone();
    arrB[2] = 5;
    
    System.out.println(arrA[2]);    // 3
    System.out.println(arrA);       // [I@c818063
    System.out.println(arrB);       // [I@3f0ee7cb

    이번에는 arrB[2]를 변경했음에도 arrA[2]에는 영향이 없는 것을 확인할 수 있다.

    또한 두 배열을 각각 출력해보면 서로 다른 주소값을 참조하고 있음을 알 수 있다.



2차원 배열의 복사

2차원 이상의 배열에서는 clone()을 사용해도 깊은 복사가 아닌 얕은 복사가 되는것에 주의하자!

  • 얕은 복사 : 단순 배열 변수의 선언 또는 clone() 메서드를 이용한다.

    int[][] arrA = {{1, 2, 3}, {5, 6, 7}};
    int[][] arrB = arrA;
    int[][] arrC = arrA.clone();
    arrB[0][2] = 5;
    arrC[1][2] = 10;
    
    System.out.println(arrA[0][2]);     // 5
    System.out.println(arrA[1][2]);     // 10
    System.out.println(arrA);           // [[I@c818063
    System.out.println(arrB);           // [[I@c818063
    System.out.println(arrC);           // [[I@3f0ee7cb

    위의 코드처럼 변수의 선언으로 복사한 arrBclone()을 통해 복사한 arrC 모두 원본인 arrA의 값에 영향을 주는것을 확인할 수 있다.


    그런데 한가지 이상한 점이 있다.

    얕은 복사를 했을 arrC 의 주소값이 arrAarrB와는 다르게 나온다는 것이다. 이는 1차원 배열에서의 clone() 복사를 잘 생각해보면 알 수 있는데, 1차원 배열의 깊은 복사에서는 배열 안의 값을 그대로 복사해 새로운 주소값을 가진 배열을 만드는 것이다.

    이를 그대로 2차원 배열의 clone() 복사에 적용해보면 배열 안의 값 (1차원 배열)을 그대로 복사해 새로운 주소값을 가진 배열을 만들기 때문에 주소값이 다르게 나온 것이다. 직관적으로 배열의 내용물은 그대로 가져오고 껍데기만 새로 만든다고 보면 될 것 같다.

    그 증거로 아래 코드를 보면 arrAarrC 내부의 1차원 배열들의 주소값은 모두 같음을 확인할 수 있다.

    int[][] arrA = {{1, 2, 3}, {5, 6, 7}};
    int[][] arrB = arrA;
    int[][] arrC = arrA.clone();
    arrB[0][2] = 5;
    arrC[1][2] = 10;
    
    System.out.println(arrA[0]);    // [I@c818063
    System.out.println(arrC[0]);    // [I@c818063
    
    System.out.println(arrA[1]);    // [I@3f0ee7cb
    System.out.println(arrC[1]);    // [I@3f0ee7cb

    따라서 내부의 1차원 배열의 주소값이 같기 때문에 clone()을 사용해도 원본 배열의 값에 영향을 주는 것이다.


  • 깊은 복사

    그렇다면 2차원 배열의 깊은 복사는 어떻게 할까? 두 가지 방법이 있다. (행의 길이가 다른 이중배열은 고려하지 않는다.)

    (물론 아래의 두가지 방법은 모두 1차원 배열의 깊은복사에서도 사용 가능하다.)

    • 2중 for문을 통한 복사

      int[][] arrA = {{1, 2, 3}, {5, 6, 7}};
      int row = arrA.length;
      int column = arrA[0].length;
      int[][] arrB = new int[row][column];
      
      for (int i = 0; i < row; i++) {
        for (int j = 0; j < column; j++) {
          arrB[i][j] = arrA[i][j];
        }
      }
      
      arrB[0][2] = 5;
      System.out.println(arrA[0][2]);     // 3

      arrB의 값 변경이 원본인 arrA에게 영향이 없음을 확인할 수 있다.


    • System.arraycopy() 를 통한 복사

      System.arraycopy(원본 배열, 원본 배열 시작위치, 새 배열, 새 배열 시작위치, 길이) 와 반복문을 이용해 복사하는 것이다.

      int[][] arrA = {{1, 2, 3}, {5, 6, 7}};
      int row = arrA.length;
      int column = arrA[0].length;
      int[][] arrB = new int[row][column];
      
      for (int i = 0; i < row; i++) {
        System.arraycopy(arrA[i], 0, arrB[i], 0, arrB[i].length);
      }
      
      arrB[0][2] = 5;
      System.out.println(arrA[0][2]);     // 3

      이 역시 위와 마찬가지로 arrB의 값 변경이 원본인 arrA에게 영향이 없음을 확인할 수 있다.

      마찬가지로 Arrays.copyOf()System.arraycopy() 를 래핑한 메서드이기 때문에 성능은 같다.


    둘의 성능 면에서 차이를 보면 System.arraycopy()Arrays.copyOf()이 반복문을 사용해 복사한것 보다 두배가량 빠르다고 한다.

    자세한 성능의 차이에 대한 테스트는 Stack overflow 사이트 에서 확인할 수 있다.

    가독성 면에서도 copy 라는 이름이 명시된 메서드를 사용하는것이 훨씬 좋으니 반복문을 통한 복사 대신 메서드사용을 지향하자!

0개의 댓글