230828 TIL #175 이차원 배열 clone / 얕은 복사 / 깊은 복사

김춘복·2023년 8월 28일
0

TIL : Today I Learned

목록 보기
175/571

Today I Learned

그래프 관련 코테 문제를 풀면서 이차원 배열을 다루는 일이 많아졌다. 문제를 풀다보면 원본 이차원 배열은 그대로 두고 clone을 해서 새롭게 구성해야 되는 경우가 있었는데, 그냥 이중 for문으로 복사를 하면서 이거 더 간단하거나 효율적인 방법이 없을까에 대해 고민했다. 그래서 알아보니 이차원 배열 복사는 깊은 복사와 얕은 복사로 나누어지는데 기초적인 내용이지만 이에 대해 한 번 정리해두려 한다.


먼저 정리!

  • clone()은 기본적으로 얕은 복사
  • 기본형 변수는 clone()을 써도 깊은 복사가 된다.(1차원 배열에서)
  • 참조형 변수는 얕은 복사가 수행되므로 독립성을 유지하기 위해선 clone을 override해서 새롭게 정의하던가 다른 방법으로 카피를 해야한다.
  • 이차원 배열은 행 별로(기본형), 혹은 2중 for문으로 요소별로 복사를 해야 깊은 복사가 가능하다.

Clone()

java.lang.Object 클래스에 정의되어 객체의 복사본을 생성하는 메서드

  • 모든 클래스는 기본적으로 Object 클래스를 상속받기 때문에 이 메서드를 쓸 수 있다.

  • 기본동작은 해당 객체의 얕은 복사를 수행한다. 객체의 필드값은 복사되지만, 필드가 참조타입인 경우 참조 값만 복사된다.

  • 따라서, 원본의 참조타입 필드가 변하면 복사본의 필드도 변한다.

  • 이를 피해 깊은 복사를 구현하려면, clone() 메서드를 오버라이드하여 객체 내부의 모든 참조 필드도 복사하는 로직을 추가해야 한다.


얕은 복사

객체의 복사를 수행할 때 객체 자체만 복사하는 것.

  • 객체 내부에 포함된 참조형 필드는 그 주소 값만 복사한다.

  • 원본객체와 복사된 객체 내부의 참조형 필드는 동일한 객체를 가르킨다.

  • 이차원 배열을 clone() 으로 복사할 경우 내부의 배열은 주소 값만 복사된다. 따라서 원본에서 내부 배열을 수정하면 복사본 배열도 같이 수정된다.

깊은 복사

깊은 복사(deep copy)는 객체를 복사할 때 그 객체의 모든 내부 내용까지 완전히 독립적으로 복사하는 것

  • 원본과 복사본이 완전히 독립적으로 분리되어 있다.

  • 원본 객체의 내부 참조 객체까지 모두 새롭게 복사한다.

  • clone()은 기본적으로 얕은 복사를 수행하므로 기본형 변수의 복사가 아닌이상 override 같은 방법을 사용해야 깊은 복사를 수행할 수 있다.

  • 이차원 배열을 깊은 복사 하려면 기본형은 각 행별로 복사를 수행하면 되고, 참조형은 각 요소 하나하나를 다 복사해야 한다.

  • 단, String은 참조형이지만 immutable, 즉 불변하므로 기본형처럼 행별로 복사를 해도 독립성을 가진다.

코드 예시

  • 1차원 기본형 배열과 2차원 기본형 배열의 clone()
    1차원 기본형 배열은 깊은 복사의 효과가 난다. 하지만 2차원 배열은 전체를 바로 clone할 경우 얕은 복사가 된다.
public class J_ArrayCopy {

    public static void main(String[] args) {
        // 1차원 기본형 배열 clone 복사
        int[] A = {1,2,3,4,5};
        int[] B = A.clone();

        A[0] = 50;
        System.out.println("B[0]의 값은 " + B[0]);
        // 출력값 1. 바뀌지 않음

        // 2차원 배열 clone 얕은 복사
        int[][] C = {{1,2,3}, {4,5,6}};
        int[][] D = C.clone();

        C[0][0] = 300;
        System.out.println("D[0][0]의 값은 " + D[0][0]);
        // 출력값 300. 원본 수정시 바뀜
        
        // 2차원 배열 깊은 복사
        C[0][0] = 1;
        int[][] E = new int[C.length][];
        
        for(int i=0; i<C.length; i++){
        	E[i] = C[i].clone();
        }
        
        C[0][0] = 500;
        System.out.println("E[0][0]의 값은 " + E[0][0]);
        // 출력값 1. 원본과 독립적.
        
    }
}
  • 1차원 참조형 배열의 clone()
    참조형 객체 내부의 변수를 변경하면 복사본도 함께 변한다.
public class ArrayCloneExample {
    static class MyObject {
        int value;
        MyObject(int value) {
            this.value = value;
        }
    }

    public static void main(String[] args) {
        MyObject[] originalArray = { new MyObject(1), new MyObject(2) };
        MyObject[] clonedArray = originalArray.clone();

        // 원본 배열의 첫 번째 객체의 값을 변경
        originalArray[0].value = 10;

        System.out.println(clonedArray[0].value);  
        // 출력값은 10. 원본에서 수정해서 복사본에서도 같이 바뀜.
    }
}
profile
Backend Dev / Data Engineer

0개의 댓글