📌 해당 글은 필자가 개인공부한 글입니다. 저작권 및 내용에 문제가 있으면 댓글남겨주세요!
자바를 처음 배울 때, 메소드 인자로 사용 되는 객체는
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등의 변수가 파라미터로 던져지면 주소
를 던진다.
전자는 깊은 복사
, 후자는 얕은 복사
로 이해하면 된다.
이번엔 자바스크립트에서 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로 나누어져 있지만 실질적으로 하나의 주소번지를 갖는다.
얕은 복사의 개념은 해당 객체의 구성요소를 그대로 복사해 새로 할당하는 것을 말한다.
'값'이 아닌 '주소 값'을 복사
복사한 후에 원본값이 변겨오디면 복사한 값도 변경
=
연산자는 얕은 복사를 수행
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객체의 값도 바뀌게 된다.
완벽하게 깊은 복사를 하려면 어떻게 해야 될까?
'값을'을 새로운 메모리 공간에 복사
복사한 후에 원본 값이 변경되어도 복사한 값은 유지
얕은 복사에 비해 메모리 소요가 크고 속도가 느리다.
깊게 복사하기 위해선, 몇 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