본문 바로가기

스터디/다재다능 코틀린 프로그래밍

5. 콜렉션 사용하기

다재다능 코틀린 프로그래밍 책으로 코틀린 스터디를 진행하면서 발표를 위해 준비했던 글입니다.


콜렉션의 특징

  • Java의 뮤터블 콜렉션 인터페이스는 코틀린에서 이뮤터블 읽기전용 인터페이스와 뮤터블 읽기 - 쓰기 인터페이스 2개의 인터페이스로 나뉘어 졌다.
  • JDK 제공 함수 이외에 몇 가지 편리한 메소드를 제공한다.

코틀린이 제공하는 편리한 메소드들

  • withIndex()
val names = listOf("Tom", "Jerry")
println(names.javaClass)
for ((index, value) in names.withIndex()) {
    println("$index $value")
}

// ...ArrayList
// 0 Tom
// 1 Jerry
  • withIndex()는 IndexedValue 라는 특별한 반복자(iterator)를 리턴
  • 이외에도 정말 많은 메소드가 존재, API 문서, 참고

페어와 트리플 사용하기

println(Pair("Tom", "Jerry")) // (Tom, Jerry)
println(mapOf("Tom" to "Cat", "Jerry" to "Mouse")) // {Tom=Cat, Jerry=Mouse}
  • to() 확장 함수는 코틀린의 모든 객체에서 사용이 가능하며 Pair 인스턴스를 만든다.
val airportCodes = listOf("LAX", "SFO", "PDX", "SEA")
val temperatures = 
    airportCodes.map { code -> code to getTemperatureAtAirpot(code) }
for (temp in temperatures) {
    println("Airport: ${temp.first}: Temperature: ${temp.second})
}
  • airportCodes가 가지고있던 공항코드를 (코드, 온도) 꼴의 Pair로 도치시켰다.
  • airportCodes는 List에서 List<Pair<String, String>>이 되었다.
  • java였다면 Map을 만들거나 클래스를 새로 만드는 등 짐이 늘어났을 것이다. → 코드를 더 적게 사용하면서 타입 안정성을 가져갈 수 있다.
  • Pair, Triple은 코틀린 스탠다드 라이브러리에 이 있는 하나의 클래스일 뿐이며 이뮤터블 하다.
  • 만약 3개 이상의 값을 그룹핑하고 싶다면 데이터클래스를 만들어라. 7-5에서 다룰 예정

객체 배열과 프리미티브 배열

  • Array 클래스는 코틀린의 배열을 상징
  • 배열은 낮은 수준의 최적화가 필요할 때만 사용, 그 외에는 List 같은 자료구조를 사용하라.
  • String 배열 만들기
val friends = arrayOf("Tintin", "Snowy")
println(friends::class) // class kotlin.Array
println(friends.javaClass) // ...String
println("${friends[0]} and ${friends[1]}") // Tintin and Snowy
  • 인덱스 연산자인 []을 사용하면 get()메소드를 호출한다. 인덱스 연산자가 왼쪽에 있다면 set()메소드를 호출한다.
  • 정수 배열 만들기
val numbers = arrayOf(1, 2, 3)
println(numbers::class) // class kotlin.Array
println(numbers.javaClass) // class [Ljava.lang.Integer;
  • Wrapper 클래스로 박싱되면서 발생하는 오버헤드를 피하기 위해 intArrayOf() 같은 메소드를 사용하자.
    • Java IntStrem이나 OptionalInt 랑 비슷한 이유
  • Array 클래스에 함수들 사용해보기
val numbers = arrayOf(1, 2, 3)
println(numbers.size) // 3
println(numbers.average()) // 2.0

리스트 사용하기

  • 이뮤터블 리스트는 listOf()로
  • 뮤터블 리스트는 mutableListOf()로
  • List에 접근할 시 []연산자 사용이 가능하다. get() 보다 노이즈가 적고 편리하다. [] 연산자를 사용하자.
  • 콜렉션에 값이 있는지 없는지 확인하기 위해선 contains() 메소드나 in 연산자를 사용할 수 있다.
val fruits: List<String> = listOf("Apple", "Banana", "Grape")
println(frutis.contains("Apple") // true
println("Apple" in fruits) // true
  • in 연산자가 더 표현력이 좋고 직관성 있다.
  • kotlin.Collections.List 인터페이스는 Java의 Arrays.asList()로 만든 JDK 객체의 뷰로 동작 → 이뮤터블함
  • 다른 element를 추가하고 싶다면 + 연산자를 사용할 수 있다.
val fruits: List<String> = listOf("Apple", "Banana", "Grape")
val fruits2 = fruits + "Orange"
println(fruits2) // [Apple, Banana, Grape, Orange]
  • element를 제거하고 싶다면 - 연산자를 사용할 수 있다.
val fruits: List<String> = listOf("Apple", "Banana", "Grape")
val fruits2 = fruits - "Apple"
println(fruits2) // [Banana, Grape]
  • 제거하려는 요소가 없다면 아무일도 일어나지 않는다.
  • fruits의 인터페이스는 List이고 구현클래스는 JDK ArrayList이다.
  • 충분한 의논이 끝난후에도 뮤터블 리스트가 만들고 싶다면 mutableListOf() 메소드를 사용해서 만들 수 있다.
  • mutableListOf() 메소드 대신 arrayListOf() 함수를 사용해서 ArrayList의 참조를 직접 획득할 수도 있다.

셋 사용하기

  • 이뮤터블 set은 setOf()로
  • 뮤터블 set은 mutableSetOf()로
  • HashSet은 hashSetOf()로
  • LinkedHashSet은 linkedSetOf()로
  • TreeSet은 sortedSetOf()로
  • setOf()는 java.util.LinkedHashSet으로 취급된다.

맵 사용하기

  • 이뮤터블 map은 mapOf()로
  • 뮤터블 map은 mutableMapOf()로
  • HashMap은 hashMapOf()로
  • LinkedHashMap은 linkedMapOf()로
  • SortedMap은 sortedMapOf()로
  • 키-값 페어가 to() 확장 함수로 만들어지며 to() 확장함수는 코틀린의 모든 객체에서 사용 가능
  • mapOf()는 Pair<K, V>를 인자로 취급
val cars = mapOf(1 to "K3", 2 to "K5");
val car: String = cars.get(3) // 오류
  • Java와 다르게 존재하지 않는 key를 get할 때 동작하지 않는다.
  • 아래와 같이 작성하면 nullable 타입을 리턴하며 정상적으로 동작하는데 6-2에서 설명 예정
val cars = mapOf(1 to "K3", 2 to "K5");
val car: String? = cars.get(3) // 정상 동작
  • immutable 하더라도 참조하는 객체가 바뀔 수 있으므로 완전 thread-safe 하지는 않다?