리스트의 복사 두 번째인 List.copyOf에 대해 알아보자!
저번 글에서 Collections.unmodifiableList에 대해 다뤘으므로 이 글을 읽기 전에 먼저 읽으시면 이해가 잘 될 겁니다!
이전 글: https://zerotoinfinite.tistory.com/8
Java - List의 복사 (1): Collections.unmodifiableList
우아한 테크코스를 진행하면서 받았던 리뷰와 찾아서 공부했던 내용을 바탕으로 적었다. 공부하는 건 많아지는데 쉽게 까먹을까 봐 블로그에 쓰기로 다짐.. 총 3가지의 리스트 복사에 대해서 다
zerotoinfinite.tistory.com
리뷰어님의 리뷰를 받아 리팩토링을 한 나의 코드는 다음과 같았다.
<변경 전>
public List<Car> getCars() {
return Collections.unmodifiableList(cars);
}
<변경 후>
public List<Car> getCars() {
return List.copyOf(cars);
}
List.copyOf는 Collections.unmodifiableList와 다르게 새로운 리스트를 만들어 객체를 담기에 원본을 통한 수정, 삽입, 삭제로 부터 자유로울 것 같아 위와 같이 수정하였다.
하지만 돌아온 리뷰어님의 답변은.. (두구두구두구)
리스트보다는 리스트가 담고 있는 '항목'에 대해 이야기하는 거였어요 :)
리스트 자체가 아니라 리스트가 담고 있는 항목을 얘기하시는 거였다.. 핀트를 잘못 잡았네요..
리스트 내의 항목 또한 복사하는 것은 다음 글에서 다룰 예정이고, 지금은 List.copyOf에 대해 다뤄보겠다!
2. List.copyOf
List.copyOf는 List에서 제공하는 static method이다.
첫 번째로 Collection을 받아서 listCopy의 인자로 다시 전달해주어 반환하고 있다. 그러면 listCopy에 들어가 보자!
if문이 아니라 else문을 중점적으로 보면된다!
인자로 받은 Collection을 배열로 바꾼다음 다시 List.of를 통해 리스트로 변환하여 반환해준다.
즉, List.copyOf는 리스트안의 원소들로 다시 리스트를 생성하여 반환한다.
반환된 리스트는 다음과 같은 특성을 띈다.
List.of로 생성한 리스트는 내부적으로 set, add, remove와 같은 method를 호출할 시 UnsupportedOperationException을 던지기 때문에 사용하면 안된다.
Collections.unmodifiableList와 List.copyOf의 가장 큰 차이점은 전자는 내부에서 원본 리스트를 참조하고 있고 우리가 다루고 있는 후자는 리스트를 벗기고 다시 리스트를 씌워 새로운 리스트를 만들었다는 점이다.
즉, 원본 리스트와 List.copyOf로 복사한 리스트는 다르다!
원본과 같은 리스트를 내부에서 참조하는 Collections.unmodifiableList의 단점을 극복하여 완벽할 것 같지만, 여전히 문제점은 남아있다.
List.copyOf의 문제점
1. 리스트 안의 객체에 대한 복사가 이루어지지 않았기 때문에 원본과 같은 객체를 참조하여 List.copyOf를 통해 받은 리스트에서 객체에 접근하여 수정하면, 원본의 수정이 이루어질 수 있다.
단순히 리스트안의 원소를 꺼낸 뒤 다시 리스트를 씌운거기 때문에, 복사된 리스트안의 객체의 참조는 원본과 동일하다.
즉 원본 리스트안의 객체를 받아와 수정하거나 복사된 리스트안의 객체를 받아와 수정하게 되면 서로 영향을 받는다는 것이다.
코드로 보면 다음과 같다.
List<Car> cars = new ArrayList<>();
cars.add(new Car("firstCar"));
// List.copyOf를 이용한 복사
List<Car> copiedCars = List.copyOf(cars);
// 복사본에서 객체 수정
copiedCars.get(0).setName("FIRSTCAR");
System.out.println("Original: " + cars.get(0).getName());
복사본에서 객체를 얻어 수정하였고 원본의 객체 또한 변경이 되었는지 확인하였다.
예상한대로 차의 이름은 "firstCar"가 아닌 "FIRSTCAR"로 변경되어 출력되었다.
회고
List.copyOf는 Collections.unmodifiableList와는 다르게 새로운 리스트를 만들어 그 안에 객체를 담는다.
그렇기에 원본 리스트에 새로운 원소를 추가하거나 기존의 원소를 제거하여도 복사본의 리스트는 그대로였다.
하지만 리스트 안의 객체를 복사한 것이 아니기 때문에 참조는 똑같다.
그렇다면 언제 List.copyOf를 써야할까?
Collections.unmodifiableList와 기능이 비슷하지만, 원본 리스트가 수정될 수 있는 상황이고, 복사본은 그 영향을 받지 않아야 한다면 List.copyOf를 고려해볼 수 있다.
또한 리스트가 담고 있는 객체가 그 안의 상태를 변경하지 않는 불변 객체 일 때 일일이 그 안의 객체를 복사할 필요가 없으므로 객체를 복사하는 시간과 메모리를 줄일 수 있는 이점이 있다.
다음은 stream을 이용하여 리스트안에 있는 객체까지 복사하는 방식을 다뤄볼 것이다!
잘못된 내용이 전파되는 것을 경계하고 있기 때문에 최대한 Oracle docs와 내부 구현 코드를 보고 작성한 글입니다. 혹시라도 틀린 내용이 있다면 댓글 부탁드립니다. 감사합니다:)
출처
1. https://docs.oracle.com/javase/10/docs/api/java/util/List.html
'프로그래밍 언어 > Java' 카테고리의 다른 글
Java - List의 복사 (3): Stream API을 활용한 DeepCopy (0) | 2023.03.26 |
---|---|
Java - List의 복사 (1): Collections.unmodifiableList (2) | 2023.03.01 |
Java - Iterator (0) | 2022.11.29 |
Java - Collections Class (0) | 2022.11.28 |
Java - Map과 HashMap (0) | 2022.11.24 |