깊은 복사 와 얕은 복사

랏 뜨·2024년 12월 9일

🔎 Overview

  코딩테스트를 준비하던 중, 그동안은 유야무야 넘겼던 배열의 복사에 관한 메서드를 쓸 일이 생겼다.

  문제를 푸는데는 별 문제가 없었지만, 이 기회에 깊은 복사와 얕은 복사에 대해서 다시 한 번 정확하게 짚고가야 할 것 같다고 생각되어 이 포스트를 작성하게 되었다.


1️⃣ 얕은 복사 (Shallow Copy)

  • 객체의 필드 값을 복사하지만, 배열 같은 참조형 필드에 대해서는 객체의 참조만 복사
  • 즉, 복사된 객체와 원본 객체가 같은 참조를 공유

특징

1) 기본 데이터 타입의 필드

  • 값이 복사되며, 원본 배열의 값이 변경되도 변경되지 않음
  • ex) int , double , char ...

2) 참조 데이터 타입의 필드

  • 객체의 참조 주소만 복사
  • 복사된 객체와 원본 객체는 같은 참조 데이터를 공유
  • 원본 배열의 값이 변경되면, 복사된 배열의 값 또한 변경됨
  • ex) String , 배열 , 객체 ...
int[] originArr = {1, 2, 3, 4, 5};
int[] copiedArr = originArr;
  • 이 경우, copiedArr 는 originArr 의 주소를 가리키고 있으므로, originArr 와 copiedArr 의 값이 변경될 시 서로 영향을 받음

2️⃣ 깊은 복사 (Deep Copy)

  • 객체의 모든 필드 값을 복사, 참조형 필드의 경우 해당 객체도 새롭게 복사
  • 복사본과 원본은 완전히 독립적

특징

1) 기본 데이터 타입의 필드

  • 값이 복사되며, 원본 배열의 값이 변경되도 변경되지 않음
  • ex) String , 배열 , 객체 ...

2) 참조 데이터 타이브이 필드

  • 별도의 새 객체를 생성하여 복사
  • 원본과 복사본은 참조 데이터를 공유하지 않음
  • 따라서, 서로 값이 변경되더라도 영향을 주지 않음

사용 방법

1) clone()

int[] originArr = {1, 2, 3, 4, 5};
int[] copiedArr = originArr.clone();

2) Arrays.copyOf()

int[] originArr = {1, 2, 3, 4, 5};
int[] copiedArr = Arrays.copyOf(originArr, originArr.length);
  • Arrays.copyOf(원본배열, 복사할 길이)
  • Arrays.copyOfRange(원본배열, 복사 시작 인덱스, 복사 끝 인덱스)

3) System.arraycopy()

int[] originArr = {1, 2, 3, 4, 5};

int size = originArr.length;  // 배열의 길이

int[] copiedArr = new int[size];
System.arraycopy(originArr, 0, copiedArr, 0, size);  // 전체 복사
  • System.arraycopy(원본배열, 복사 시작 인덱스, 복사 후 저장할 배열, 복사 시작 인덱스, 복사할 데이터 길이)
  • 위의 코드의 경우, originArr 의 0번 인덱스부터 size 만큼을 복사해서 copiedArr 의 0번 인덱스부터 붙여넣음
  • originArr 로부터 복사되지 않는 copiedArr 의 요소는 그대로 0
  • 복사할 길이가 원본 배열의 크기보다 크다면, ArrayIndexOutOfBoundsException 이 발생
  • 복사 받을 배열의 크기가 복사할 길이보다 작다면, 같은 에러 발생
  • 이 메서드의 경우, 2차원 배열에서 사용하기에도 용이함

3️⃣ 얕은 복사와 깊은 복사의 차이점

특징얕은 복사깊은 복사
복사 범위필드 값만 복사, 참조형 필드는 참조 주소만 복사모든 필드와 참조형 객체까지 새로 복사
참조 공유 여부원본 객체와 복사된 객체가 참조형 필드를 공유원본 객체와 복사된 객체가 독립적
변경 영향참조형 필드의 변경이 원본과 복사본에 모두 영향을 미침원본과 복사본이 서로 영향을 주지 않음

🤔 두 복사는 각각 언제 사용할까?

1) 얕은 복사

  • 빠른 복사가 필요하고, 기본형 데이터 타입의 값을 복사할 경우
  • 참조형 필드의 변경이 없을 것으로 예상되는 경우

2) 깊은 복사

  • 객체의 독립성이 중요한 경우
  • 값을 복사하되, 이후 값이 변경될 가능성이 존재할 경우

📝 System.arraycopy 와 Arrays.copyOf

두 메서드 모두 배열의 깊은 복사를 하고 싶을 때 사용한다. 그렇다면 성능면에서, 어떤 메서드를 사는 것이 더 좋을까?

1) System.arraycopy()

  • 성능
    • 네이티브 코드로 구현
    • 배열 복사 같은 기본적인 작업에 매우 빠른 성능
    • 메모리 블록을 한 번에 복사하는 방식
  • 사용 예시
    • 특정 범위 복사
    • 부분 복사
  • 사용 상황
    • 성능이 중요한 상황에서 주로 사용

2) Arrays.copyOf()

  • 성능
    • System.arraycopy() 를 내부적으로 호출
    • 새로운 배열 생성 및 원본 배열 복사 - 기본값 채우기 의 작업이 포함
  • 사용 예시
    • 배열의 크기를 변경하면서 복사
    • 보다 동적인 작업 가능
  • 사용 상황
    • 배열의 크기의 변경이 필요할 경우 주로 사용
    • 가독성 면에서의 이점이 있음

결론

  • 단순 배열 복사의 역할로써는 System.arraycopy() 가 조금이라도 더 빠름
    • Arrays.copyOf() 는 내부적으로 System.arraycopy() 를 호출하기 때문
    • System.arraycopy() 는 저수준에서 최적화된 네이티브 코드로 구현되어 있음

  • 하지만 성능 면에서 드라마틱하게 큰 차이는 없음
    • 가독성을 중요시하여 Arrays.copyof() 를 사용해도 큰 문제가 없음
    • System.arraycopy() 와는 다르게 Arrays.copyof() 는 배열의 크기를 동적으로 조절할 수 있기 때문에, 이런 면에서 더욱 이점이 있음

참고) OpenAI. (2024).ChatGPT(4o)[Large language model].https://chatgpt.com/

profile
기록

0개의 댓글