본문 바로가기
프로그래밍/JAVA

Java 리스트(List), Map 의 깊은 복사 완벽 정리 (+ 예제 코드)

by 곰 옥수수 2025. 3. 3.
728x90
반응형

Java에서 Map, List, String의 깊은 복사 방법

Java에서 Map, List, String을 깊은 복사하는 방법을 살펴보겠습니다.


1. ArrayList 깊은 복사

방법 1: for 루프 사용 (가장 기본적인 방법)

List<Item> deepCopy = new ArrayList<>();
for (Item item : originalList) {
    deepCopy.add(new Item(item.name)); // 개별 객체 복사
}
  • 장점: 간단하고 빠름.
  • 단점: 코드가 길어질 수 있음.

방법 2: stream() + map() 사용 (람다식 활용)

List<Item> deepCopy = originalList.stream()
        .map(item -> new Item(item.name)) // 새로운 객체 생성
        .collect(Collectors.toList());
  • 장점: 한 줄로 간결하게 작성 가능.
  • 단점: stream() 사용으로 가독성이 떨어질 수 있음.

방법 3: clone() 사용 (Cloneable 인터페이스)

class Item implements Cloneable {
    String name;

    Item(String name) {
        this.name = name;
    }

    @Override
    protected Object clone() {
        return new Item(this.name); // 새로운 객체 반환
    }
}

List<Item> deepCopy = new ArrayList<>();
for (Item item : originalList) {
    deepCopy.add((Item) item.clone()); // clone() 사용
}
  • 장점: 객체 복사가 필요할 때 유용.
  • 단점: Cloneable 인터페이스를 구현해야 하며, clone() 은 예외 처리가 필요할 수 있음.

2. HashMap 깊은 복사

방법 1: for 루프 사용 (기본적인 방법)

Map<Integer, Item> deepCopy = new HashMap<>();
for (Map.Entry<Integer, Item> entry : originalMap.entrySet()) {
    deepCopy.put(entry.getKey(), new Item(entry.getValue().name)); // 새로운 객체 복사
}
  • 장점: 가장 직관적이고 빠름.
  • 단점: 코드가 길어질 수 있음.

방법 2: stream() 사용 (람다식)

Map<Integer, Item> deepCopy = originalMap.entrySet().stream()
        .collect(Collectors.toMap(
                Map.Entry::getKey,
                entry -> new Item(entry.getValue().name) // 새로운 객체 복사
        ));
  • 장점: 간결한 코드.
  • 단점: stream() 사용으로 가독성이 떨어질 수 있음.

방법 3: clone() 사용 (Cloneable 인터페이스)

Map<Integer, Item> deepCopy = new HashMap<>();
for (Map.Entry<Integer, Item> entry : originalMap.entrySet()) {
    deepCopy.put(entry.getKey(), (Item) entry.getValue().clone()); // clone() 사용
}
  • 장점: 객체가 많을 경우 관리하기 용이.
  • 단점: Cloneable 구현 필요.

Map<K, List<T>> 깊은 복사 방법

방법 1:  for 루프를 사용한 깊은 복사 (기본적인 방법)

Map<Integer, List<Item>> deepCopy = originalMap.entrySet().stream()
    .collect(Collectors.toMap(
        Map.Entry::getKey,
        entry -> entry.getValue().stream()
                      .map(item -> new Item(item.name)) // 개별 객체 복사
                      .collect(Collectors.toList())
    ));
  • 장점: 가장 직관적이고 성능이 좋음.
  • 단점: 코드가 조금 길어질 수 있음.

방법 2:   stream() + map() 사용 (람다식 활용)

Map<Integer, List<Item>> deepCopy = originalMap.entrySet().stream()
    .collect(Collectors.toMap(
        Map.Entry::getKey,
        entry -> entry.getValue().stream()
                      .map(item -> new Item(item.name)) // 개별 객체 복사
                      .collect(Collectors.toList())
    ));
  • 장점: 한 줄로 간결하게 작성 가능.
  • 단점: stream() 사용으로 가독성이 떨어질 수 있음.

방법 3:  clone() 사용 (Cloneable 인터페이스 활용)

class Item implements Cloneable {
    String name;

    Item(String name) {
        this.name = name;
    }

    @Override
    protected Object clone() {
        return new Item(this.name);
    }
}

Map<Integer, List<Item>> deepCopy = new HashMap<>();

for (Map.Entry<Integer, List<Item>> entry : originalMap.entrySet()) {
    List<Item> copiedList = new ArrayList<>();
    for (Item item : entry.getValue()) {
        copiedList.add((Item) item.clone()); // clone() 사용
    }
    deepCopy.put(entry.getKey(), copiedList);
}
  • 장점: clone() 을 사용하여 복사할 때 가독성이 높아짐.
  • 단점: Cloneable 인터페이스를 구현해야 함.

함수로 깊은 복사는 불가능한가?

답! 가능은 하다. 

 

1) 객체(참조형 데이터)가 없고, 오직 기본형(Primitive Type) 또는 String(불변 객체) 으로 구성된 경우
→ System.arraycopy(), Arrays.copyOf(), HashMap.clone() 만으로도 완전한 깊은 복사 가능

 

2) 객체(참조형 데이터)가 포함된 경우
얕은 복사가 되므로 추가적인 객체 복사가 필요

 

 

 

1. ArrayLIst로 기본 자료형 배열 및 String 배열 깊은 복사

System.arraycopy()Arrays.copyOf()객체 배열이 아닌 기본형 배열 또는 String 배열의 경우 완전한 깊은 복사가 가능하다.

●copyOf  (기본형 배열 복사 → 깊은 복사 O)

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

// 깊은 복사 (값을 완전히 새로 복사)
int[] deepCopy = Arrays.copyOf(originalArray, originalArray.length);

// deepCopy 값을 변경해도 originalArray 에 영향 없음
deepCopy[0] = 99;

System.out.println(Arrays.toString(originalArray)); // [1, 2, 3, 4, 5]
System.out.println(Arrays.toString(deepCopy));     // [99, 2, 3, 4, 5]
  • 기본 자료형 배열(int, double, char 등)은 값 자체를 복사하므로 깊은 복사.
  • System.arraycopy() 사용해도 같은 결과.

arraycopy () (String 배열 복사 → 깊은 복사 O)

String[] originalArray = {"A", "B", "C"};

// 깊은 복사 (새로운 배열 생성)
String[] deepCopy = Arrays.copyOf(originalArray, originalArray.length);

// deepCopy 값 변경 (String은 불변 객체이므로 변경해도 원본 영향 없음)
deepCopy[0] = "Z";

System.out.println(Arrays.toString(originalArray)); // [A, B, C]
System.out.println(Arrays.toString(deepCopy));     // [Z, B, C]
  • String 은 불변 객체(Immutable Object)이므로 새로운 String 값을 대입해도 원본에 영향이 없음.

2. HashMap.clone() (Key-Value 구조의 깊은 복사)

Key, Value 가 모두 불변 객체(기본형, String) 로만 구성된 경우, clone() 으로 깊은 복사가 가능.

HashMap.clone() (Key-Value가 String으로만 구성 → 깊은 복사 O)

HashMap<String, String> originalMap = new HashMap<>();
originalMap.put("A", "Apple");
originalMap.put("B", "Banana");

HashMap<String, String> deepCopy = (HashMap<String, String>) originalMap.clone();

// deepCopy 값 변경
deepCopy.put("A", "Avocado");

// 원본은 영향 없음
System.out.println(originalMap.get("A")); // Apple
System.out.println(deepCopy.get("A"));    // Avocado
  • String 은 불변 객체이므로 clone() 으로도 완전한 깊은 복사가 가능.

정리

☞ 장점, 단점

방법 ArrayList HashMap
for 루프 빠르고 직관적 코드가 길어짐
stream() 간결한 코드 stream() 가독성
clone() 객체 복사 용이 Cloneable 구현 필요

 

결론

  • 가장 간단한 방법: for 루프
  • 깔끔한 코드: stream()
  • 객체 복사 관리가 필요하면: clone()
  • 함수로 깊은 복사가 가능하다, 참조형 데이터(객체)가 포함 되어 있다면 불가능하다.

  추천 방법

  • 일반적으로 for 루프 or stream() 사용
  • 복잡한 경우 clone() or Serialization 고려

 

★용도에 맞게 잘 사용해보도록 하자!

728x90
반응형

댓글