티스토리 뷰

전개

1) 백준 17135 캐슬 디펜스를 풀던 도중 복사된 List원소를 변경했을 때, 원본 List의 원소가 동시에 변경이 되는 일이 발생.

2) 그로인해 깊은 복사와 얕은 복사의 차이에 대하여 더 자세하게 정리해야 할 필요성을 느낌.

3) 나아가 자바에서 깊은 복사를 하려면 어떤식으로 접근해야하는지 알아보기.

 

얕은 복사 (Shallow Copy)

1) 주소값을 복사한다. 참조하는 실제값이 같다. 복사본의 변경이 원본에 영향을 미친다.

 

깊은 복사 (Deep Copy)

1) 실제값을 새로운 메모리 공간에 복사한다 (원본이 참조하는 객체까지 복사한다.). 실제값이 다르다. 복사본의 변경이 원본에 영향을 미치지 않는다.

 

예시 코드

package com.baekJoon;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

public class AAA {
	static BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
	static List<Integer> list = new ArrayList<>();
	static List<Integer> list2 = new ArrayList<>();
	static List<String> list3 = new ArrayList<>();
	static List<String> list4 = new ArrayList<>();
	static List<Node> list5 = new ArrayList<>();
	static List<Node> list6 = new ArrayList<>();
	
	public static void main(String[] args) throws NumberFormatException, IOException {
		setList();
		setList2();
		setList3();
		copyList();
		copyList2();
		copyList3();
		changeCopyList();
		changeCopyList2();
		changeCopyList3();
		printList();
		printList2();
		printList3();
	}
	
	//
	private static void setList() {
		for(int i=0; i<5; i++) {
			list.add(i);
		}
	}
	private static void setList2() {
		for(int i=0; i<5; i++) {
			list3.add(Integer.toString(i));
		}
	}
	private static void setList3() {
		for(int i=0; i<3; i++) {
			list5.add(new Node(i, i, i, false));
		}
	}
    
	//
	private static void copyList() {
		list2.addAll(list);
	}
	private static void copyList2() {
		list4.addAll(list3);
	}
	private static void copyList3() {
		list6.addAll(list5);
	}
    
	//
	private static void changeCopyList() {
		list2.add(5);
		list2.set(3, 33);
		list2.remove(4);
	}
	private static void changeCopyList2() {
		list4.set(3, list.get(3)+"zz");
		list4.remove(4);
	}
	private static void changeCopyList3() {
		list6.get(2).flag = true;
		list6.add(new Node(3,3,3,false));
		list6.get(3).flag = true;
		list6.remove(0);
	}
	
	//
	private static void printList() {
		System.out.println("원본 list");
		System.out.println(list);
		System.out.println("복사된 list2");
		System.out.println(list2);
		System.out.println();
	}
	private static void printList2() {
		System.out.println("원본 list3");
		System.out.println(list3);
		System.out.println("복사된 list4");
		System.out.println(list4);
		System.out.println();
	}
	private static void printList3() {
		System.out.println("원본 list5");
		System.out.println(list5);
		System.out.println("복사된 list6");
		System.out.println(list6);
		System.out.println();
	}
	
	static class Node{
		int r;
		int c;
		int d;
		boolean flag;
		public Node(int r, int c, int d, boolean flag) {
			super();
			this.r = r;
			this.c = c;
			this.d = d;
			this.flag = flag;
		}
		@Override
		public String toString() {
			return "[r=" + r + ", c=" + c + ", d=" + d + ", flag=" + flag + "]";
		}
	}
}

 

실험 결과

1) 먼저 Integer 형과 String 형의 List에는 해당되지 않는다는 것을 확인할 수 있다.

ㄴ 여기서 addAll()은 primitive 타입이나 immutable 한 자료형에서는 DeepCopy가 된다는 것을 알 수 있다.

2) 객체형 List 에서 복사된 List의 원소를 변경할 때 (flag = true), 원본의 값도 변경되는 것을 볼 수 있다.

   ㄴ 즉, list5가 갖고 있는  Node 의 값 자체가 아니라 Node의 주소값이 list6에 저장되었다는 의미이다.

3) 하지만 list5를 복사한 List6에서 remove()를 해도 원본 List인 list5의 원소가 삭제되지는 않는다. <- 뭐지?

   ㄴ 검색 및 질문 결과 객체가 제거 되는 것이 아니라 

 

그럼 깊은 복사는 어케함?

결론 부터 말하면 자바에서 깊은 복사를 제공해주는 API는 존재하지 않다. 즉 개발자가 알아서 구현해야 하는 것이다.

1) 복사 생성자 <- 가장 무난한 방법

static class Node{
	int r;
	int c;
	int d;
	boolean flag;
	public Node(int r, int c, int d, boolean flag) {
		super();
		this.r = r;
		this.c = c;
		this.d = d;
		this.flag = flag;
	}
	// 복사 생성자
    public Node(Node node){
    	this.r = node.r;
        this.c = node.c;
        this.d = node.d;
        this.flag = node.flag;
    }
}
for(int i=0; i<list5.size(); i++) {
	Node front = list5.get(i);
	list6.add(new Node(front));
}

ㄴ 이제 list6을 변경해도 list5가 변하지 않는다.

2) New 로 일일이 생성 : '캐슬 디펜스'를 풀 때 사용한 방법이다.

3) Cloneable 인터 페이스를 통한 clone() 재정의 <- 특정한 케이스가 아니면 추천하지 않는다고 한다.

 

후기

 

 캐슬 디펜스의 스노우볼이 이렇게 커질 줄은 몰랐다.

문제를 단순히 푸는것 보다 중요한 것은 그 문제를 푸는 도중 발견하는 새로운 문제점에 직면하고 대처하는 자세라고 생각한다. 

오늘 이후로 다시는 같은 문제로 고생하지 않길 바라면 글을 마친다.

'Programming' 카테고리의 다른 글

[JAVA] 정렬에 대한 고찰 (Arrays.sort() 와 Collections.sort())  (2) 2021.08.22
[Programming] Maven과 Gradle  (1) 2021.08.22
[Java] ???  (0) 2021.08.05
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함