[코틀린인액션] 코틀린에서 컬렉션 만들기

컬렉션 사용

fun main() {
    hashSetOf(1, 7, 53).forEach {
        println(fizzBuzz(it))
    }
    //public fun <T> hashSetOf(vararg elements: T): HashSet<T> = elements.toCollection(HashSet(mapCapacity(elements.size)))
    //@SinceKotlin("1.1") public actual typealias HashSet<E> = java.util.HashSet<E>
    
    arrayListOf("10", "11", "1001").forEach {
        println(it.toInt())
    }
    
    //public fun <T> arrayListOf(vararg elements: T): ArrayList<T> =
    //    if (elements.size == 0) ArrayList() else ArrayList(ArrayAsCollection(elements, isVarargs = true))
    
    //@SinceKotlin("1.1") public actual typealias ArrayList<E> = java.util.ArrayList<E>
    
    
    hashMapOf(1 to "one", 7 to "seven", 53 to "fifty-three").forEach {
        println("${it.key} -> ${it.value}")
    }
    //public fun <K, V> hashMapOf(vararg pairs: Pair<K, V>): HashMap<K, V> = HashMap<K, V>(mapCapacity(pairs.size)).apply { putAll(pairs) }
    
    //@SinceKotlin("1.1") public actual typealias HashMap<K, V> = java.util.HashMap<K, V>
}
  • Java 에 존재하는 해시셋, 어레이리스트, 해시맵에 alias 를 각각 만들어서
  • hashMap Set ArrayList 의 자료구조에 맞게 데이터를 넣어주는 방식으로 구현이 되어있다.
    • 자바에서 번거로운 코드들을 다루기 쉽게 포장했다고 생각하면 된다.

it 은 뭐지?

  • it 은 매개변수를 참조하는 데 사용된다고 한다.
  • 일단 arrayList 의 경우 항상 같은 타입의 객체가 들어가는건 누구나 아는 사실이다.
    • 해당 타입을 코틀린에서 추론을 할 수 있으며,
    • 추론된 요소에 대한 매개변수를 it 라는 변수로 지정할 수 있다.

그럼 hashMapOf(1 to "one", 7 to "seven", 53 to "fifty-three") 에서 to는 뭐야? 새로운 문법인가?

public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)
  • infix 는 중위 표현식이라고 한다.
    • 아까 보았던 1 to “one” 같은
      • 피연산사 1 , “one” 사이에 위치할 수 있도록 해주는 표현식의 일부인 것이다.
    • 두 개의 매개변수를 받아서 쌍(Pair) 를 생성한다.
    • to 는 코틀린 표준 라이브러리의 확장 함수 라고 한다.
  • this , that
    • A타입의 객체와 B 타입의 객체를 각각 의미한다.
    public data class Pair<out A, out B>(
        public val first: A,
        public val second: B
    ) : Serializable {
    • Pair 라는 DTO 에 단순히 A 와 B를 채워주는 주는 것이다.

hashMapOf(vararg pairs: Pair<K, V>):

  • hashMapOf 에 들어가는
    • vararg 는 가변인자라고 한다.
    • 자바에서 매개변수에 pairs … 이라는 요소와 동일한 역할을 하는 것같다.
  • pairs 라는 가변인자를 받아서
  • putAll 에서
    /**
     * Puts all the given [pairs] into this [MutableMap] with the first component in the pair being the key and the second the value.
     */
    public fun <K, V> MutableMap<in K, in V>.putAll(pairs: Array<out Pair<K, V>>): Unit {
        for ((key, value) in pairs) {
            put(key, value)
        }
    }
    • Pairs 를 N 시간만큼의 반복을 통해
    • Map 에 키 밸류 값으로 담아주게 된다.

단순하게 그냥 자바코드로 구현하는 것 보다야 뭔가 단점이 있지 않을까 라는 생각을 하게 되지만,,

  • 엄청나게 좋아진 컴퓨팅 사양들과
  • 최적화된 로직을 통한 소스

등을 고려하면 이 생각이 틀려먹었다고 생각이 든다.

자바 코드도 처음부터 이런 소스코드는 아니였지 않겠는가..

  • 내가 짜는 것 보다 천재들이 짜놓은 방식이 더 좋을 수 밖에 없겠지

코틀린 컬렉션 확장기능

  • 자바 컬렉션에 없는 유용한 확장 함수도 제공한다
    • 리스트의 마지막 원소를 들고오는 last() 함수
      fun main() {
          val arrayListOf = arrayListOf("10", "11", "1001")
          println(arrayListOf.last())
      }
      //1001
      • last() 내부 소스 코드
        public fun <T> List<T>.last(): T {
            if (isEmpty())
                throw NoSuchElementException("List is empty.")
            return this[lastIndex]
        }
        • lastIndex 로 단순 조회
        public val <T> List<T>.lastIndex: Int
            get() = this.size - 1
        • lastIndex 는 현재 컬렉션안의 원소 size 에서 단순 -1
    • 수의 집합에서 최댓값을 찾는 max() 함수
      fun main() {
          val arrayListOf = arrayListOf("10", "11", "1001").map { it.toInt() }
          println(arrayListOf.max())
      }
      //1001
      • max() 내부 소스
        @Deprecated("Use maxOrNull instead.", ReplaceWith("this.maxOrNull()"))
        @DeprecatedSinceKotlin(warningSince = "1.4", errorSince = "1.5", hiddenSince = "1.6")
        @Suppress("CONFLICTING_OVERLOADS")
        public fun <T : Comparable<T>> Iterable<T>.max(): T? {
            return maxOrNull()
        }
        • maxOrNull() 소스
          @SinceKotlin("1.4")
          public fun <T : Comparable<T>> Iterable<T>.maxOrNull(): T? {
              val iterator = iterator()
              if (!iterator.hasNext()) return null
              var max = iterator.next()
              while (iterator.hasNext()) {
                  val e = iterator.next()
                  if (max < e) max = e
              }
              return max
          }
          • 단순히 리스트를 순회하면서 최댓값을 기억하고
          • 반환하는 형태인 것 같다.

Uploaded by N2T