코틀린이 제공해주는 콜렉션의 진화는 두 가지. 함수 확장과 뷰
뷰
- Java의 대부분의 콜렉션은 뮤터블임. 하지만 최근 몇년간 Java는 이뮤터블 콜렉션을 선보였지만, 같은 인터페이스를 구현하고 있다는 문제점을 갖고 있음 → Runtime에 UnsupportedOperationException이 나옴
- 이와 달리 코틀린은 연산이 불가능하다는 것을 실행 시간이 되어서야 알리지 않음 → 그래서 뷰가 존재
- 리스트, 셋, 맵은 각기 뷰를 두 가지씩 갖고 있음. read-only view로 통하는
immutable view
, read-write view로 불리기도 하는mutable view
- 예로 List(read-only)와 MutableList(read-write view)는 코틀린의 ArrayList 뷰임
- 주의사항 : 읽기전용 뷰가 thread safety를 제공해 준다고 가정 x. 읽기 전용 뷰가 참조하는 콜렉션은 뮤터블 콜렉션임. 다른 스레드가 해당 콜렉션을 변경할 수도 있음
페어와 트리플 사용하기
println(Pair("Tom", "Jerry")) // (Tom, Jerry) println(mapOf("Tom" to "Cat", "Jerry" to "Mouse")) // {Tom=Cat, Jerry=Mouse}
- 첫번째 줄은 Pair의 생성자를 이용해서 인스턴스 생성
- 두번째 줄은 to() 확장함수를 이용해 Map의 엔트리가 될 페어를 만듦. to() 확장함수는 코틀린의 모든 객체에서 사용이 가능
객체 배열과 프리미티브 배열
- Array<T> 클래스는 코틀린의 배열을 상징함. 배열은 낮은 수준의 최적화가 필요할 때만 사용하도록 하고, 그 외에는 List 같은 다른 자료구조 사용하기
val frineds = arrayOf("Tintin", "Snowy", "Haddock", "Calculus") println(friends::class) // class kotlin.Array println(friends.javaClass) // class [Ljava.lang.String; val numbers = arrayOf(1, 2, 3) println(numbers::class) // class kotlin.Array println(numbers.javaClass) // class [Ljava.lang.Integer; // Integer 배열이 아닌 int 배열을 만들기 위해서는 intArrayOf() 메서드를 사용 val numbers = intArrayOf(1, 2, 3) println(numbers::class) println(numbers.javaClass) // class [I // Array의 생성자는 파라미터로 // 1. 배열의 사이즈 // 2. 0부터 시작하는 인덱스를 받아 해당 위치에 있는 값을 리턴해주는 함수 // 를 받음 prinrtln(Array(5) { i -> (i + 1) * (i + 1) }.sum()) // 55
리스트 사용하기
immutable view
val fruits: List<String> = listOf("Apple", "Banana", "Grape") println(fruits) // [Apple, Banana, Grape] println("first's ${fruits[0]}, that's ${fruits.get(0)}") // first's Apple, that's Apple
- 인덱스 연산자를 사용하는 편이 get() 보다 노이즈가 적고 편리하다. get() 대신 []를 사용하기
- collection에 대해서 값이 있는지 없는지 확인하기 위해서는 contains() 메서드를 사용하거나 in 연산자를 사용할 수 있다.
println(fruits.contains("Apple")) // true println("Apple" in fruits) // true
val fruits: List<String> = listOf("Apple", "Banana", "Grape") println(fruits::class) // class java.util.Arrays$ArrayList println(fruits.javaClass) // class java.util.Arrays$ArrayList fruits.add("Celery") //inferunit.kts:7:8: error: unresolved reference 'add'. // + 와 - 연산자로 해당 리스트에서 요소를 추가, 제외하고 새로운 리스트를 만들 수 있음 val fruits2 = fruits + "Orange" println(fruits) //[Apple, Banana, Grape] println(fruits2) // [Apple, Banana, Grape, Orange] val noBanana = fruits - "Banana" println(noBanana) // [Apple, Grape]
mutable view
val fruits: MutableList<String> = mutableListOf("Apple", "Banana", "Grape") fruits.add("Orange")
- mutableList는 변경이 가능함
- 가능한 mutableListOf() 보다는 listOf() 를 사용하자. 변경 가능 객체는 좋지 않다.
- 뮤터블 리스트를 만들어야 겠다는 생각이 든다면 당장 주변에 도움의 손길을 요청하라. 그러면 분명 뮤터블 리스트를 만들지 말라고 할 것이다. 충분한 생각과 의논이 끝난 후에도 뮤터블 리스트를 만드는 것이 올바른 선택이라는 판단이 든다면 mutableListOf() 함수를 이용해서 리스트를 만들 수 있다.
list to map
// associate val list = listOf("apple", "banana", "cherry") val map = list.associate { it to it.length } // 키: 요소, 값: 요소의 길이 println(map) // {apple=5, banana=6, cherry=6} // associateBy val list = listOf("apple", "banana", "cherry") val map = list.associateBy { it.first() } // 키: 첫 글자, 값: 요소 println(map) // {a=apple, b=banana, c=cherry} // associateWith val list = listOf("apple", "banana", "cherry") val map = list.associateWith { it.length } // 키: 요소, 값: 요소의 길이 println(map) // {apple=5, banana=6, cherry=6}
Set 사용
- setOf() → Set<T>인스턴스
- mutableSetOf() → MutableSet<T>
- hashSetOf() → java.util.HashSet<T>
- linkedSetOf() → LinkedHashSet
- sortedSetOf() → TreeSet
List<T> 처럼 Set<T>와 MutableSet<T>에는 +, -, contains, in 등 많은 함수들이 있음
Map 사용
- mapOf() → Map<K, V >의 읽기전용 맵
- mutableMapOf() → MutableMap<K, V>
- hashMapOf()
- linkedMapOf()
- sortedMapOf()