Java Collection의 Unmodifiable과 Immutable

Jazz Avenue·2022년 10월 10일
1

페이스북에서 본 이일민님의 타임라인 글을 보고 정리 차원에서 다시 작성해본다.

Java 9 이전의 불변 컬렉션 생성하기

  • Java 9 이전에 Immutable Collection을 만들기 위한 코드는 다음과 같다.
Set<String> set = new HastSet<>();
// ... add some elements in set
set = Collections.unmodifiableSet(set);
  • 이 때 만들어지는 오브젝트들을 확인하면 다음과 같다.
    1. HashMap 1개 (HashSet 내부에서 생성)
    2. HashSet 1개
    3. Unmodifiable Wrapper Object 1개
    4. Node Object (HashMap 내부에서 생성하는 Node 객체로 저장하는 Item의 개수만큼 생성)
    5. Initaial Capacity 만큼의 Node[] 1개 (HashMap 내부에서 객체를 저장하는 table)
  • Java에서 Object는 64Bit JVM을 기준으로 12Byte의 헤더를 가진다.
  • Java Object Layout 이라는 라이브러리를 활용해서 객체의 대략적인 크기를 측정해보면 다음과 같다.


Java 9의 Set.of를 이용한 불변 컬렉션 생성

  • Java 9에서 도입된 Set.of()를 사용해서 불변 컬렉션을 생성해보자.
  • 이렇게 생성된 Set은 내부적으로 Set12 인스턴스를 참조하고 여기서 직접 저장하는 객체들을 참조한다.
  • 내부적으로 사용 및 만들어지는 오브젝트의 수가 훨씬 적고 참조 구조도 단순하기 때문에 결과적으로는 Java Object Layout을 통해 확인한 결과가 앞선 방법보다 약 1/3 정도 줄어들었다.


Unmodifiable 과 Immutable은 다르다

  • 이일민님의 글의 마무리에는 다음과 같이 적혀있다.

    "생성후 변경은 필요없고(+불가능해야 하고) 참조만 한다면 기존의 Collections.unmodifiableSet() 대신 컬렉션 인터페이스의 static 메서드로 추가된 of()로 생성하면 좋다.

  • 이와 관련해서 좀 더 부가적인 설명을 하기 위해서 Unmodifiable과 Immutable은 다르다는 것을 짚고 넘어가려고한다.
  • Collections.unmodifiableSet() 이 동작하는 방식을 살펴보면 다음과 같이 set(), add(), addAll() 등의 변경을 가하는 메서드를 호출하면 내부적으로 UnsupportedOperationException을 발생시키도록 되어있다.
  • 이 때 문제가되는 부분은 Collections.unmodifiableSet() 등을 통해 생성된 객체가 아닌 변경 가능한 원본 객체를 통해 접근하게되면 Unmodifiable Collection도 변경된다. (원본 자체에 대한 수정을 막을 수 없음)
  • 즉, "불변"이라는 목적을 완벽하게 달성할 수는 없다. (Unmodifiable Collection을 원본 객체에 할당한다고 하더라도 할당되기 이전의 원본 객체의 레퍼런스를 가지고 있다면 변경이 가능함)
  • 이 때, Unmodifiable Collection을 이용해서 불변 컬렉션을 만들고 싶다면 읽기 전용 복사본을 활용해서 다음과 같이 코드를 작성해야한다.
Set<String> immutable = Collections.unmodifiableSet(new HashSet<>(set));
  • Java 9의 Set.of() 등은 원본 컬렉션을 참조하는 방식이 아닌 애초에 값들을 가지고 새로운 불변 컬렉션을 생성하기 때문에 불변성을 달성할 수 있다.
  • 다만, 주의할 점은 컬렉션 내부에 저장되어있는 레퍼런스 타입의 객체들의 속성은 외부에 있는 원본 객체들을 통해서 값이 변경될 수 있고, 이를 방지하고 싶다면 해당 객체를 값 타입(불변 객체)로 선언하던지 방어적 복사본을 활용하자.

결론을 3줄 요약해보자면

  • 컬렉션의 불변성을 달성하기 위해서라면 unmodifiableCollection() 대신 Collection의 of() 를 활용하자. (좀 더 쉽고, 내부적으로도 가벼운 불변 컬렉션을 만들 수 있다.)
  • 이를 통해서 달성할 수 있는 것은 컬렉션의 불변성이지 내부에 저장하고 있는 참조 객체들의 불변성은 아니다.
  • 내부에 저장하는 객체들의 불변성까지도 달성하고 싶다면, 해당 객체를 불변 객체로 선언하던지 방어적 복사본을 활용하자.

0개의 댓글

관련 채용 정보