-
[Java] Shallow copy와 Deep copyJava 2022. 1. 2. 23:30
코드를 짜다보면 배열이나 객체를 복사해야 하는 경우가 발생한다. 이때 실수로 복사를 잘못할 경우 이슈가 생길 수 있기 때문에 주의해야 한다. 이를 위해 얕은 복사와 깊은 복사의 차이점에 대해 명확하게 이해하고 넘어가고자 한다.
얕은 복사(Shallow copy)와 깊은 복사(Deep copy)
얕은 복사(Shallow copy)는 주소값을 복사(스택 영역)하기 때문에 참조하고 있는 실제 값은 같다.
깊은 복사(Deep copy)는 실제 값을 메모리 공간에 복사(힙 영역)하기 때문에 참조하고 있는 실제 값이 다르다.
이해를 쉽게 하기 위해 코드로 알아보도록 하자.
다음과 같이 이름, 나이를 가지는 Person 클래스가 있다고 가정해보자.
public class Person { String name; int age; public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
얕은 복사(Shallow copy)
void shallowCopy() { Person person = new Person("홍길동", 25); Person copyPerson = person; copyPerson.setName("홍길은"); copyPerson.setAge(26); }
위의 코드와 같이 이름은 홍길동, 나이는 25세인 Person 객체의 인스턴스 person을 생성하고 이를 copyPerson에 복사한다. 그리고 copyPerson의 이름을 홍길은, 나이를 26세로 수정한다.
우리는 person은 홍길동, 25 personCopy는 홍길은, 26이길 기대하지만 결과는 두 인스턴스 모두 홍길은, 26을 가지게 된다.
System.out.println(person); // 홍길은, 26 System.out.println(copyPerson); // 홍길은, 26
그 이유는 위의 이
Person copyPerson = person;
코드에서 person의 참조값을 복사했기 때문이다.
메모리 공간의 그림을 통해 알아보면 아래 그림과 같다.즉, copyPerson은 Heap 영역에 따로 값을 복사한 것이 아니라 person이 참조하고 있는 값을 복사한 것이다.
여기서 copyPerson의 값을 수정해보자.
copyPerson.setName("홍길은"); copyPerson.setAge(26);
copyPerson의 값을 수정할 경우 copyPerson이 참조하고 있는 즉, person과 같이 공유하고 있는 데이터의 값이 수정된다. 하지만 우리는 실제 데이터를 복사하기를 기대한다.
이를 위해서는 깊은 복사(Deep copy)를 해야 한다.
깊은 복사(Deep copy)
깊은 복사를 구현하는 방법에는 대표적으로 3가지 방법이 있다.
- 복사 생성자 또는 복사 팩토리를 이용하여 복사하는 방법
- 직접 객체를 생성하여 복사하는 방법
- Clonable 인터페이스을 구현하여 clone() 함수를 오버라이딩하여 복사하는 방법
clone() 함수를 재정의하는 방법은 final 클래스가 아닌 경우 문제가 발생할 수 있다.
자세한 내용은 https://jackjeong.tistory.com/m/30 이 곳을 참고하자.1. 복사 생성자 또는 복사 팩토리를 이용하여 복사하는 방법
// 복사 생성자 public Person(Person person) { this.name = person.name; this.age = person.age; } // 복사 팩토리 public static Person copyFactory(Person person) { Person copyPerson = new Person(person.name, person.age); return copyPerson; }
2. 직접 객체를 생성하여 복사하는 방법
void deepCopy() { Person person = new Person("홍길동", 25); Person copyPerson = new Person(person.getName(), person.getAge()); }
이처럼 깊은 복사를 통해 객체를 복사할 경우 참조값을 복사하는 것이 아니라 실제 데이터를 복사한 것을 확인할 수 있다.
copyPerson.setName("홍길은"); copyPerson.setAge(26); System.out.println(person); // 홍길동, 25 System.out.println(copyPerson); // 홍길은, 26
배열을 복사하는 여러가지 메서드
앞에서는 객체를 복사하는 방법에 대해 알아보았다. 자바에서 1차원 배열에 한해서는 복사를 쉽게 할 수 있는 여러가지 메서드를 제공하고 있다.
Object.clone()
public class Array_Copy{ public static void main(String[] args) { int[] a = { 1, 2, 3, 4 }; int[] b = a.clone(); } }
Arrays.copyOf()
Arrays.copyOf() 함수는 배열의 시작점부터 원하는 길이까지 배열을 복사한다.
import java.util.Arrays; public class Array_Copy{ public static void main(String[] args) { int[] a = { 1, 2, 3, 4 }; int[] b = Arrays.copyOf(a, a.length); // src arr, length } }
Arrays.copyOfRange()
Arrays.copyOfRange() 함수는 원하는 시작점부터 원하는 길이까지 배열을 복사한다.
import java.util.Arrays; public class Array_Copy{ public static void main(String[] args) { int[] a = { 1, 2, 3, 4 }; int[] b = Arrays.copyOfRange(a, 1, 3); // src arr, src pos, length } }
System.arraycopy()
System.arraycopy() 함수는 지정된 배열을 대상 배열의 지정된 위치에 복사한다.
public class Array_Copy{ public static void main(String[] args) { int[] a = { 1, 2, 3, 4 }; int[] b = new int[a.length]; System.arraycopy(a, 0, b, 0, a.length); // src arr, src pos, dst arr, dst pos, length } }
Reference
https://jackjeong.tistory.com/100
https://coding-factory.tistory.com/548
'Java' 카테고리의 다른 글
[Java] Serialization과 Deserialization (0) 2022.01.03 [Java] String, StringBuffer, StringBuilder (0) 2022.01.03 [Java] Identity와 Equality (0) 2022.01.03 [Java] Primitive type과 Reference type (0) 2022.01.03 [Java] Error와 Exception (1) 2022.01.02