자바 공부#2_06. Immutable 컬렉션?
Immutable 객체(불변 객체)?
불변 객체는 이름 그대로, 한번 생성되면 그 내부 값을 변경할 수 없는 객체를 의미한다.
불변 객체의 장점
- 쓰레드에 안전하여 멀티 쓰레드 환경에서 동기화를 고려하지 않아도 된다.
- 불변 객체는 어떤 스레드도 다른 스레드에게 영향을 줄 수 없으므로 안심하고 공유할 수 있다.
- 불변객체를 필드로 사용할 때 방어적 복사 (defensive-copy) 가 필요없다.
- 불변객체는 내부상태가 변경되지 않으므로, Map Key 와 Set 요소로 사용하기에 적합하다.
- 불변객체를 한번 메모리에 할당하게 되면 같은 객체를 계속 호출하여도, 새롭게 할당하지 않아도 되므로 GC의 성능을 높일 수 있다.
불변 객체 생성 전략
- setter 메소드를 사용하지 마라
- 모든 필드를 final 과 private를 사용해서 선언해라
- 클래스를 final 로 선언하여 Overriding을 막아라
- 객체를 생성하기 위한 생성자 혹은 정적 팩토리 메소드를 추가해라
- 인스턴스 필드에 가변객체가 포함된다면 방어적 복사를 이용하여 전달해라
이렇게 불변 객체에 대해서 어느 정도 이해했으면, 불변 컬렉션으로 넘어가겠다.
Immutable Collection
불변 컬렉션(Immutable Collection)은 컬렉션의 불변성을 보장하며 element를 추가/삭제할 수 없다.
단, 불변성은 컬렉션에만 해당하며 element의 참조나 내부 값은 변경할 수 있다.
import java.util.*;
class Person { ... }
public class Example {
public static void main(String[] args) {
List<Person> people = new ArrayList<>();
people.add(new Person("Alice"));
people.add(new Person("Bob"));
// 불변 컬렉션 생성
List<Person> immutablePeople = Collections.unmodifiableList(people);
// 요소 추가/삭제 시도 → 예외 발생
// immutablePeople.add(new Person("Charlie")); // ❌ UnsupportedOperationException
// 요소 내부 값 변경 → 가능
immutablePeople.get(0).changeName("Eve"); // ✅ 가능
System.out.println(immutablePeople); // [Eve, Bob]
}
}
완전한 불변을 원한다면 요소 자체(위의 경우에서는 Person)도 immutable 객체로 만들어야 함.
(참고: https://dev-cool.tistory.com/23)
불변 컬렉션 생성(Set을 예시로)
불변 컬렉션을 생성하는 데에는 보통 Collections.unmodifiableSet(), Set.of() 두 가지 방법을 사용한다.
Java 9 이전: Collections.unmodifiableSet()
Java 9 ~: Set.of()
주로 자바 버전에 따라 이렇게 쓰는 것 같다.
1. unmodifiableSet()
unmodifiableSet() 을 사용해 Immutable Collection을 만들기 위한 코드는 다음과 같다.
Set<String> set = new HastSet<>();
// ... add some elements in set
set = Collections.unmodifiableSet(set);
2. Set.of()
Set.of() 을 사용해 Immutable Collection을 만들기 위한 코드는 다음과 같다.
Set<String> set = Set.of("A", "B", "C"); // 변경 불가한 리스트
Immutable 컬렉션 사용 예시
📌 1. 컨트롤러 응답에 불변 리스트 사용
@GetMapping("/tags")
public ResponseEntity<List<String>> getTags() {
List<String> tags = tagService.findAllTags();
return ResponseEntity.ok(List.copyOf(tags));
}
List.copyOf(...)는 외부 리스트를 방어적으로 복사해 불변 리스트로 만든다.
📌 2. 상수로 고정된 리스트 만들기
public class Constants {
public static final List<String> REGIONS = List.of("서울", "부산", "제주");
}
📌 3. 생성자에서 불변 컬렉션 저장
public class UserDto {
private final List<String> roles;
public UserDto(List<String> roles) {
this.roles = List.copyOf(roles); // 방어적 복사
}
public List<String> getRoles() {
return roles;
}
}
❗ 주의점
| List.of() | 최대 10개 요소까지만 Map.of() 지원 (그 이상은 Map.ofEntries() 사용) |
| Null 불가 | List.of(null) → NullPointerException 발생 |
| 내부 데이터 수정 불가 | list.add(...), set.remove(...) 등 → UnsupportedOperationException 발생 |
결론
| 컨트롤러 응답 반환 | ✅ List.copyOf() |
| 상수 설정 값 | ✅ List.of(...), Map.of(...) |
| 동적으로 값 추가/삭제 | ❌ ArrayList, HashMap 등 mutable 컬렉션 사용 |
| 멀티스레드 환경 | ✅ 데이터 공유 용도에 적합 |
+ 추가로 unmodifiable의 immutable 여부에 대해서는
참고하기.
참고:
- https://bibibim.tistory.com/17
- https://roomenergy.tistory.com/11
- https://sas-study.tistory.com/413
더 깊게 공부하기
- https://hello-judy-world.tistory.com/209