List/MutableList vs PersistentList/ImmutableList의 차이점

SSY·2025년 4월 20일
0

Kotlin

목록 보기
12/12
post-thumbnail
개념표준 Kotlin 컬렉션JetBrains Immutable 컬렉션
읽기 전용 (Read-only)ListImmutableList
변경 가능 (Mutable)MutableList없음 (❌)
구조적 불변 (Persistent)없음 (❌)PersistentList

1. List/MutableList

List는 읽기 전용 객체이며, MutableList는 읽고 쓰기 전용 객체이다. 따라서 List는 add/addAll/remove등의 메서드 호출이 불가한 반면, MutableList는 가능하다.

MutableList의 수정작업 시, 객체 자체가 변한다. 따라서 이를 외부로 공유하는 작업은 내부 상태의 예상치 못한 변경으로 인해 위험할 수도 있다.

이를 방지하고자, List타입 공유를 생각할 수 있다. 하지만 이 또한 100% 완벽한 방법이 아니다. 아래 코드를 보면 알다시피 'list'변수의 타입은 읽기 전용인 List타입이다. 하지만 진짜 타입은 MutableList이다. 따라서 list 변수 타입을 하위 캐스팅 후 add()호출로 내부 상태 값을 변경할 수 있는데, 이는 List타입이라고 해도 외부에 공유하는 작업이 완전히 안전하지 않다는 의미다.

fun main() {
  val list: List<Int> = mutableListOf(1,2,3)
  val list2 = list
  (list as MutableList).add(4)
  println(list) // 1,2,3,4
  println(list2) // 1,2,3,4
}

내부적으론 'list'변수만 쓰고, 외부에 'list2'변수를 공유한다 생각해보자. list2 또한 [1,2,3,4]가 나오는 위험이 있다.

2. ImmutableList/PersistentList

얼핏 보면 List타입은 ImmutableList에, MutableListPersistentList에 대응된다 생각할 수 있다. 왜냐면, 전자는 읽기 전용이며, 후자는 쓰기 전용으로 add/remove등의 메서드를 지원하기 때문이다. 하지만 이들은 차이가 있다.

일전에 List타입은 하위 캐스팅을 통해 내부 상태 값 변경에 위험이 있다했다. 하지만 ImmutableList는 하위 캐스팅을 통해 PersistentList로 될 수는 있을지언정, add/remove등 수정 작업 진행 시, 내부 상태 값이 변경되지 않는다. 해당 메서드를 호출 시, 변경된 상태가 반영 된 새로운 리스트를 반환한다.

val oldList: PersistentList<Int> = persistentListOf(1, 2, 3)
val newList = oldList.add(4) // oldList는 그대로 유지

위와 같은 동작때문에, PersistentList변경 시, '깊은 복사'를 진행한다 생각하라 수 있다. 물론, List/MutableList들끼리는 그렇지만, ImmutableList/PersistentList는 그렇지 않다. PersistentList는 구조 공유를 사용한다. 내부는 트리 구조로 돼있고, 일부 복사 후, 나머지는 공유한다.

위 코드 예시로 설명하자면,

val oldList: PersistentList<Int> = persistentListOf(1, 2, 3)
val newList = oldList.add(4) // oldList는 그대로 유지

위 코드에서 내부적으로 [1,2,3]노드를 공유하한다. 그 후, newList에는 4만 붙임으로써 성능을 최적화시킨다. 따라서 결과적으로 기존 데이터의 깊은 복사를 진행하지 않는다는 점이 특징이고, GC비용도 줄어든다. 또한 다수의 리스트를 메모리에 올릴 수 있어 메모리적으로도 효과적이다.

요약

  • List/MutableList는 내부 상태를 변경한다.
  • ImmutableList/PersistentList는 내부 상태를 변경하지 않는다. 상태를 변경할 땐, 새로운 리스트를 반환한다.
  • 반환하는 새로운 리스트는 ImmutableCollection 내부에서 노드 공유를 통해 GC비용을 최적화한다.
profile
불가능보다 가능함에 몰입할 수 있는 개발자가 되기 위해 노력합니다.

0개의 댓글