프로그래밍 언어/코틀린

Deep Copy(깊은 복사)와 Shallow Copy(얕은 복사)

닉네임못짓는사람 2023. 4. 2. 22:36
반응형

이번 글에서는 Deep Copy와 Shallow Copy에 대해서 알아보도록 하자.

 

데이터


우리가 프로그래밍 중 사용하는 데이터는 크게 두 가지 타입으로 분류할 수 있다.

  1. Value Type
  2. Reference Type

이 둘의 차이는 데이터를 메모리에 어떻게 저장하느냐에 따라 나뉘게 된다.

 

프로그램에서 사용하는 메모리 영역 중 힙(Heap)영역과 스택(Stack)영역이 있는데,

Value Type의 경우 값을 Stack영역에 저장하고, Reference Type의 경우 실제 데이터는 Stack에 저장한 뒤 해당 메모리의 주소값을 Stack영역에 저장하여 이를 통해 데이터를 불러오게 된다.

 

C언어를 배우신 분들은 포인터에 대해 아실테니 위와 같은 개념을 좀 더 잘 알고 계시리라고 생각한다.

 

Deep Copy?


위에서 이야기한 내용을 미리 숙지하고 Deep Copy와 Shallow Copy에 대해서 알아보도록 하자.

우리가 a변수에 있는 데이터를 b변수에 대입하려고 하면 가장 쉬운 방법은 아래와 같이 하는것이다.

fun main() {
    var a = 10
    var b = a
    println("a: $a, b: $b")
}

이렇게 할 경우 a의 값이 그대로 b변수에 Copy되기 때문에 두 변수는 같은 값을 가지게된다.

 

그런데 이때 b변수의 값을 변경한다면 어떻게 될까?

fun main() {
    var a = 10
    var b = a
    
    b = 20
    println("a: $a, b: $b")
}

이 경우 b의 값만이 변경되고 a의 값은 변하지 않는다.

그 이유는 a변수는 Value Type의 변수이기 때문에 var b = a라는 코드가 실행되었을 때,

메모리 상에서 Stack영역에 있는 a의 값을 복사한 뒤 Stack에 b의 영역을 할당하여

그곳에 복사한 값을 넣어주는 작업이 이루어지기 때문이다.

 

이 작업을 Deep Copy라고 한다.

 

Shallow Copy?


그러면 a가 Reference Type인 경우 어떻게 될까?

fun main() {
    var a = mutableListOf(0, 1, 2, 3, 4, 5)
    var b = a
    println("a: $a, b: $b")
}

위와 같이 이번에는 a변수에 0~5의 값을 가지는 리스트를 넣어보았다.

이 상황에서 b를 통해서 리스트의 값을 바꿔보도록 하자.

 

fun main() {
    var a = mutableListOf(0, 1, 2, 3, 4, 5)
    var b = a
    
    b[0] = 30
    println("a: $a, b: $b")
}

이번에는 위의 경우와는 달리 b에 접근해서 데이터를 변경했는데, a변수에 저장되어 있는 값까지 변경되었다.

이것은 a와 b는 Stack영역에서 각각 다른 메모리 영역을 가지고 있지만,

그 실제 값은 Heap영역에 있는 List의 메모리 주소이기 때문이다.

때문에 b변수에 접근해서 Heap에 잇는 List의 값을 변경하면 a는 그 값을 그대로 가져올 뿐이니 위와 같은 결과가 나오기 마련이다.

 

이 작업을 Shallow Copy라고 한다.

 

Class Instance로 확인


이번에는 위와 다르게 User라는 클래스를 만들어서 사용해보도록 하자.

fun main() {
    var a = User("홍길동", 20)
    var b = a
    
    println("a: ${a.name} ${a.age}, b: ${b.name} ${b.age}")
}

class User(var name: String, var age: Int) {}

위와 같이 User클래스의 인스턴스를 만들어 a에 할당하고, b에 이를 Shallow Copy해보도록 하자.

 

fun main() {
    var a = User("홍길동", 20)
    var b = a
    
   	b.name = "고길동"
    println("a: ${a.name} ${a.age}, b: ${b.name} ${b.age}")
}

class User(var name: String, var age: Int) {}

그러면 위에서 확인했던 것 처럼 a변수에 접근해서 얻는 데이터도 변한 것을 알 수 있다.

이때 각 변수를 그대로 출력할 경우 아래와 같이 나오게 된다.

 

fun main() {
    var a = User("홍길동", 20)
    var b = a
    
   	b.name = "고길동"
    println("a: $a, b: $b")
}

class User(var name: String, var age: Int) {}

그러면 두 변수가 가지고있는 User인스턴스의 hashcode가 정확히 같은 값을 가지고 있는것을 확인해 볼 수 있다.

 

그러면 이번엔 a를 Deep Copy해서 b에 할당해보도록 하자.

fun main() {
    var a = User("홍길동", 20)
    var b = User(a.name, a.age)
    
    b.name = "고길동"
    println("a: $a ${a.name} ${a.age}, b: $b ${b.name} ${b.age}")
}

class User(var name: String, var age: Int) {}

그러면 이렇게 b변수에 접근해서 데이터를 변경해도 a변수에 저장된 데이터는 변하지 않는다.

또한 두 변수에 저장된 hashcode도 다르게 나오는 것을 볼 수 있다.

반응형