본문 바로가기

Java

[Java] 배열 깊은 복사, 얕은 복사 ( feat. 원소가 객체라면? )

728x90

 

 

문제점 )

객체를 요소로 가진 A 배열이 있다. 새로운 B 배열을 만들고 A 배열의 요소들을 복사해서 저장하고 싶다. A 배열의 객체 요소를 변경해도 B 배열 내부의 요소들은 영향을 받지 않아야 한다. 어떻게 해야 할까?

 

 

해결책 )

깊은 복사를 배열 자체만 하면 안 된다. 배열 내부에 저장된 요소 하나하나 깊은 복사 해서 저장해야 한다.

 


배열을 복사하는 방법은 크게 2 가지다. 

얕은 복사 (Shallow Copy) 

얕은 복사는 해당 배열의 '주소값'을 복사하는 것이다.
따라서 원본 배열의 주소값을 복사했기 때문에 원본 배열의 요소를 변경하면
복사된 배열도 동일하게 수정된다.

int[] original = {1, 2, 3, 4, 5};
int[] copyArray = original; // 얕은 복사

original[0] = 9; // 원본 요소 수정

System.out.println(Arrays.toString(original)); // [9, 2, 3, 4, 5]
System.out.println(Arrays.toString(copyArray)); // [9, 2, 3, 4, 5]

깊은 복사 (Deep Copy) 

깊은 복사는 해당 배열의 ''을 복사하는 것이다.
주소값이 아니라 값만 복사하기 때문에 아예 새로운 배열로 만들어진다.
일반적으로 '복사'라고 하면 깊은 복사를 지칭한다.

int[] original = {1, 2, 3, 4, 5};
int[] copyArray = new int[5];

for (int i = 0; i < original.length; i++) {
	copyArray[i] = original[i]; // 깊은 복사
}

original[0] = 9; // 원본 요소 수정

System.out.println(Arrays.toString(original)); // [9, 2, 3, 4, 5]
System.out.println(Arrays.toString(copyArray)); // [0, 2, 3, 4, 5]


for문을 사용하지 않고도 '깊은 복사'를 할 수 있는 메서드가 존재한다.


Object.clone ()

int[] original = {1, 2, 3, 4, 5};
int[] copyArray = original.clone(); // 깊은 복사

original[0] = 9; // 원본 요소 수정

System.out.println(Arrays.toString(original)); // [9, 2, 3, 4, 5]
System.out.println(Arrays.toString(copyArray)); // [0, 2, 3, 4, 5]



Arrays.copyOf()

int[] original = {1, 2, 3, 4, 5};
int[] copyArray = Arrays.copyOf(original, original.length); // 깊은 복사

original[0] = 9; // 원본 요소 수정

System.out.println(Arrays.toString(original)); // [9, 2, 3, 4, 5]
System.out.println(Arrays.toString(copyArray)); // [0, 2, 3, 4, 5]



-  Arrays.copyOfRange()

int[] original = {1, 2, 3, 4, 5};
int[] copyArray = Arrays.copyOfRange(original, 0, original.length); // 깊은 복사

original[0] = 9; // 원본 요소 수정

System.out.println(Arrays.toString(original)); // [9, 2, 3, 4, 5]
System.out.println(Arrays.toString(copyArray)); // [0, 2, 3, 4, 5]



System.arraycopy()

int[] original = {1, 2, 3, 4, 5};
int[] copyArray = new int[5];

System.arraycopy(original, 0, copyArray, 0, original.length); // 깊은 복사

original[0] = 9; // 원본 요소 수정

System.out.println(Arrays.toString(original)); // [9, 2, 3, 4, 5]
System.out.println(Arrays.toString(copyArray)); // [0, 2, 3, 4, 5]


 

 


 

배열 내부의 요소가 원시형 타입이 아니라 참조형 타입, 즉 객체라면 단순히 위 메서드를 사용해선 안된다.

객체가 담긴 배열만 '깊은 복사'하더라도, 각각의 배열의 인덱스에 저장된 객체의 주소는 그대로 얕은 복사가 돼서 객체의 내용을 바꾸면 영향을 준다. 아래 코드를 보면 두 배열 내부의 객체 주소가 동일하다.

 

 

public class test {
    public static void main(String[] args) {
        A[] a = {new A(), new A(), new A()};
        A[] b = a.clone(); // 깊은 복사
        System.out.println(Arrays.toString(a));
        System.out.println(Arrays.toString(b));
    // [problem2.A@4eec7777, problem2.A@3b07d329, problem2.A@41629346]
    // [problem2.A@4eec7777, problem2.A@3b07d329, problem2.A@41629346]
    }
}

class A {
}

 

public class test {
    public static void main(String[] args) {
        A[] a = {new A(), new A(), new A()};
        A[] b = Arrays.copyOf(a, 3); // 깊은 복사
        System.out.println(Arrays.toString(a));
        System.out.println(Arrays.toString(b));
    // [problem2.A@4eec7777, problem2.A@3b07d329, problem2.A@41629346]
    // [problem2.A@4eec7777, problem2.A@3b07d329, problem2.A@41629346]
    }
}

class A {

}

 

 

따라서 배열 내부의 객체까지 모두 '깊은 복사'를 적용하고 싶다면 for 문을 이용해 배열 요소 하나씩 깊은 복사를 적용해야 한다. 아래 코드를 보면 배열의 각 요소가 가리키는 주소값이 상이하다. 비로소 진정한 깊은 복사가 이뤄졌다.

 

public class test {
    public static void main(String[] args) {
        A[] a = {new A(), new A(), new A()};
        A[] b = new A[3];

        for (int i = 0; i < a.length; i++) {
            b[i] = a[i].clone(); // 요소 하나하나 깊은 복사
        }

        System.out.println(Arrays.toString(a));
        System.out.println(Arrays.toString(b));
    // [problem2.A@4eec7777, problem2.A@3b07d329, problem2.A@41629346]
    // [problem2.A@404b9385, problem2.A@6d311334, problem2.A@682a0b20]
    }
}

class A implements Cloneable {
    public A clone() {
        try {
            return (A) super.clone();
        } catch (CloneNotSupportedException e) {}
        return null;
    }
}