깊은 복사 vs 얕은 복사에 대해서(총 정리)

지환·2024년 7월 29일
0

JAVA

목록 보기
41/41

📌 해당 글은 필자가 개인공부한 글입니다. 저작권 및 내용에 문제가 있으면 댓글남겨주세요!

자바를 처음 배울 때, 메소드 인자로 사용 되는 객체는

value가 아닌 reference를 전달한다는 교과서 구문을 읽고 가볍게 넘긴적이 있다.

다음 예시를 보자.

Person p = new Person("Jihwan");

setName(p);
System.out.println(p.getName());

p = setName(p);
System.out.println(p.getName());

private Person setName(Person p) {
	p.setName("After => Jihwan");
    return p;
}

둘 다After => Jihwan 가 출력된다.

class Person {
    private String name;
    
    public Person(String name) {
        this.name = name;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
}


public class shallowcopy {
    public static void main(String[] args) {
        Person p = new Person("jihwan");
        setAgentName(p); // 메소드 내부에서 p가 null로 설정되는 것은 메소드 내부의 로컬 변수에만 영향을 미친다.
        // Q1) 여기서 p의 값은 바뀌었을까?
        // -> 밑 p는 그대로 jihwan를 갖는 객체이다.
        System.out.println(p.getName()); 


        p = setAgentName(p);
        // Q2) 이 땐 null를 Return한다. 더 이상 Person객체를 참조하지 않는다.
        // NPE 발생
        System.out.println(p.getName());
        
    }
    private static Person setAgentName(Person p) {
        p = null;
        return p;
    }
}

1에서는 ‘noname’이 출력되고, 2에서는 NPE가 발생한다.

프로그래밍을 처음 배울 땐 이와 같은 부분이 납득되지 않았다.

첫번째 예시 코드의 1번에서는 메소드를 실행하는 것만으로도 p의 값이 변경되었는데,

두번째 예시 코드도 메소드 내부에서 p를 오염시켰으나(null)로 -> 이 경우는 영향을 받고 있지 않다.


왜 그럴까?

자바에서는 메소드의 인자로 전달되는 객체에 대해 실제 해당 객체의 값을 전달하지 않는다.

기억하자. 객체가 존재하는 메모리의 reference를 전달한다. (두번째 예시 코드의 첫번째 로그를 보면 NPE가 발생하지 않은 것을 볼 수 있다.)

이번엔 다른 예제를 보자.

public class TestJava {
    public static void main(String[] args) {
        String str = "originalJihwan";
        String[] arr = {"origin", "Jihwan"};
        System.out.println("before :" + str); // before : originalJihwan
        for(String a : arr) {
            System.out.println("before arr : " + a); 
            // before arr : origin
            // before arr : Jihwan
        }

        copyTest(str, arr); // 관점 포인트
        /* 
         * for문을 돌리는 arr의 값은 바뀌었을까?
         */

        System.out.println("after :" + str); // originalJihwan
        for(String a : arr) {
            System.out.println("after :" + a);
            // after : copy
            // after : Jihwan
        }
    }

    public static void copyTest(String str, String[] arr) {
        str = "copyJihwan";
        arr[0] = "copy";
    }
    
}

String은 타 메소드에서 변경을 해도 그대로 출력이 되는데, String[] 타 메소드에서 변경된 값이 출력되는것을 확인 할 수 있다.

기본 자료형 변수가 파라미터로 던져지면 을 던지지만, Array,List등의 변수가 파라미터로 던져지면 주소를 던진다.

전자는 깊은 복사, 후자는 얕은 복사로 이해하면 된다.


Js

이번엔 자바스크립트에서 Shallow / Deep Copy는 어떻게 이루어질까?

자바스크립트에서 객체를 복사하는 방법은 간단하다.

할당 하면 된다.

let a = {x : 1};
let b = a;

a.x = 2;

console.log(a.x); // 2
console.log(b.x); // 2

둘 다 2로 바뀐 것을 볼 수 있다. 왜 그럴까?

b=a 구문에서 b변수에 할당할 때, a 객체의 주소(reference)를 복사하기 때문이다.

간단하게 요약하면, 변수가 a,b로 나누어져 있지만 실질적으로 하나의 주소번지를 갖는다.


Shallow Copy

  • 얕은 복사의 개념은 해당 객체의 구성요소를 그대로 복사해 새로 할당하는 것을 말한다.

  • '값'이 아닌 '주소 값'을 복사

  • 복사한 후에 원본값이 변겨오디면 복사한 값도 변경

  • = 연산자는 얕은 복사를 수행

var a = {x:1,y:2};
var b = {x:a.x, y:a.y};
  • 대상 객체의 요소를 이루는 값을 그래도 복제

  • 루프를 통해 해당 객체 내부 요소들을 복사하는 함수를 만들어보도록 하자.

shallowCopy

function shallowCopy(target){
	let result = {};
  	for(let key in target) {
    	result[key] = target[key];
    }
  return result;
}

다른 방법은 없을까?

이전에 다뤘던 Object.assign({},a)를 사용해도 된다.

해당 방법을 사용해도 되지만, 치명적인 단점이 있다.

var a = {
	x :1,
  	y : {
    	ya : 1,
      	yb : 2,
    },
};
var b = Object.assign({},a);

a.y.ya = 3;

console.log(b.y.ya); // 3

2depth에 있는 객체의 요소는 레퍼런스가 할당되어 a객체의 값을 변경하게 되면 이 레퍼런스를 참조하고 있던 b객체의 값도 바뀌게 된다.

완벽하게 깊은 복사를 하려면 어떻게 해야 될까?

deepcopy

  • '값을'을 새로운 메모리 공간에 복사

  • 복사한 후에 원본 값이 변경되어도 복사한 값은 유지

  • 얕은 복사에 비해 메모리 소요가 크고 속도가 느리다.

깊게 복사하기 위해선, 몇 depth의 객체이더라도 끝까지 들어가서 해당 값을 복제 해야된다.

그렇다면 재귀를 이용해야 될 것 같은데? 라는 생각을 필연적으로 하게 된다.

function deepcopy(target) {
	if(!target || typeof target !== "object")
      return target;
  	
  	let result = {};
  	for (let key in target) {
    	result[key] = deepcopy(target[key]);
    }
return result;

}

이렇게 구현하면 생각했던대로 depth의 복사대상이 객체라면 한번 더 재귀로 파고 들어가서 복사하게된다.


Reference | https://yeonyeon.tistory.com/336

profile
아는만큼보인다.

0개의 댓글

관련 채용 정보