educative - kotlin - 5

Sung Jun Jin·2021년 3월 13일
0

Using Collections

Collection이란

Collection 객체는 여러 원소들을 담을 수 있는 자료구조를 뜻한다.
ex) DTO, Array, List, Set, Map

Types of collections in Kotlin

코틀린 Collection은 기본적으로 Mutable (read-write), immutable(read-only) 컬렉션을 별개로 지원한다. 아래와 같은 상속 구조를 가지고 있다. 코틀린에서는 동시성, 병렬 문제에서 더 안전한 immutable collection을 권장한다.

코틀린에서 제공하는 collection은 다음과 같다

  • Pair : 동일하거나 다른 데이터 유형의 두 값으로 이루어진 튜플
  • Triple : 동일하거나 다른 데이터 유형의 세 값으로 이루어진 튜플
  • Array : 인덱스로 접근가능한 고정된 길이의 객체나 값으로 이루어진 컬렉션
  • List : 정렬된 순서의 객체로 이루어진 컬렉션
  • Set : 정렬되지 않는 순서의 객체로 이루어진 컬렉션
  • Map : key와 value로 이루어진 딕셔너리

코틀린에서는 자바의 컬렉션과 함께 사용할 수 있는 다양한 함수들을 지원한다. 예를들어 withIndex() 메소드는 List의 인덱스와 값을 리턴해주는 IndexedValue라는 데이터 클래스를 반환한다. 이 데이터 클래스를 destructing한 값인 index, value를 출력해보면 다음과 같은 결과가 나타난다

val names = listOf("Tom", "Jerry")

for ((index, value) in names.withIndex()) {
	println("$index $value")
}
"""
0 Tom
1 Jerry
"""

Using Pair and Triple

코틀린에서 2개, 혹은 3개의 객체를 저장할 수 있는 튜플을 각각 Pair, Triple 타입이라고 한다.

println(Pair("Tom", "Jerry")) //(Tom, Jerry)
println(mapOf("Tom" to "Cat", "Jerry" to "Mouse")) //{Tom=Cat, Jerry=Mouse} 

// map은 key와 value로 이루어진 딕셔너리이므로 key를 사용해 value에 접근할 수 있음
val map = mapOf("Tom" to "Cat", "Jerry" to "Mouse")
println(map["Tom"]) // Cat
println(map["Jerry"]) // Mouse

to() 메소드는 Pair 인스턴스를 생성하는 코틀린의 확장함수(extension function) 이다.

>>> "Tom" to "Cat"
res85: kotlin.Pair<kotlin.String, kotlin.String> = (Tom, Cat)
>>> "Jerry" to "Mouse"
res88: kotlin.Pair<kotlin.String, kotlin.String> = (Jerry, Mouse)

예를들어 공항 코드(String)와 온도(Double)을 통해 특정 공항의 온도를 알고 싶다면 Pair를 사용해 효과적으로 구현할 수 있다.

val airportCodes = listOf("LAX", "SFO", "PDX", "SEA")

val temperatures = airportCodes.map{code -> code to getTemperatureAirport(c0de)}

for (temp in temperatures) {
  println("Airport: ${temp.first}: Temperator ${temp.second}")
}

airportCode 컬렉션을 돌면서 해당 공항의 온도를 map() 함수와 to() 함수를 통해 공항코드와 온도로 이루어진 Pair 인스턴스를 만들어준다.

Triple도 Pair의 특성과 동일하다. 예를들어 Circle에 대한 정보를 담는 데이터 클래스를 생성하고 싶다면 다음과 같이 (x, y, radius) 값을 가지고 있는 Triple 객체를 사용해줄 수 있다

val circle = Triple<Int, Int, Double>(1,1,2.5)
println(circle) //(1, 1, 2.5)

Pair, Triple 모두 immutable한 튜플이다. 만약 2,3 이상의 immutable 객체를 저장하고 싶다면 데이터 클래스를 생성해야 한다.

Creating an array in Kotlin

코틀린에서 array는 mutable한 값의 컬렉션이다. arrayOf() 함수를 사용해 인스턴스를 생성할 수 있고 [] 연산자를 통해 값에 대한 접근이 가능하다.

val friends = arrayOf("Tintin", "Snowy", "Haddock")

println(friends::class) //class kotlin.Array
println(friends.javaClass) //class [Ljava.lang.String]
println("${friends[0]}, and ${friends[1]}") //Tintin and Snowy

friends 변수는 새롭게 만들어진 Kotlin.Array = Array<T> 타입의 객체를 참조한다. 이는 String으로 이루어진 Java 배열이다.

정수형 배열 또한 똑같이 선언해주면 된다.

val numbers = arrayOf(1, 2, 3)
    
println(numbers::class) //class kotlin.Array
println(numbers.javaClass) //class [Ljava.lang.Integer;

그러나 좋은 방법은 아니다. 정수나 문자열이나 arrayOf() 함수를 통해 넘겨주면 똑같이 Array<T> 인스턴스가 생성되지만, 내부 요소들은 Integer 클래스를 참조하는 박싱된 Integer 타입이다. 단순한 정수로 이루어진 배열을 선얼할때 굳이 이렇게 선언해줄 필요가 없으므로 아예 arrayOf() 함수 대신 intArrayOf()를 사용해 배열의 요소를 원시(primitive) 타입으로 해주면 더 효율적이다

val numbers = intArrayOf(1, 2, 3) // 효율 Good
    
println(numbers::class) //class kotlin.Array
println(numbers.javaClass) //class [Ljava.lang.Integer;

Array 클래스는 내부적으로 지원해주는 편한 프로퍼트와 함수들이 있다.

println(numbers.size) //3 
println(numbers.average()) //2.0

println(Array(5) { i -> (i + 1) * (i + 1) }.sum()) //55

Creating list & Set in Kotlin

listOf() 함수를 사용하면 immutable한 리스트, mutableListOf()를 사용하면 mutable한 리스트가 생성된다. 똑같이 [] 연산자를 사용해 배열의 요소에 대하 접근이 가능하다. contains() 메소드 혹은 in 연산자를 통해 값에 존재 여부를 확인할 수 있다.

val fruits: List<String> = listOf("Apple", "Banana", "Grape")
println(fruits) //[Apple, Banana, Grape]

// lists.kts
println("first's ${fruits[0]}, that's ${fruits.get(0)}") 
//first's Apple, that's Apple
  
println(fruits.contains("Apple")) //true 
println("Apple" in fruits) //true

listOf() 함수를 사용하면 immutable한 리스트를 생성하기 때문에 다음과 같이 리스트 안에 add()를 통해 값을 추가하려고 하면 컴파일 오류가 발생한다. 따라서 배열의 요소를 추가, 혹은 삭제하고 싶다면 + , -연산자를 사용하면 된다. 이 연산자를 사용하면 원래 리스트를 복사한 다음, 추가 & 삭제된 값을 반영해주는 새로운 리스트가 생성된다..

val fruits: List<String> = listOf("Apple", "Banana", "Grape")
fruits.add("Orange") //ERROR: unresolved reference: add

val fruits: List<String> = listOf("Apple", "Banana", "Grape")
val fruits2 = fruits + "Orange"

println(fruits) //[Apple, Banana, Grape] 
println(fruits2) //[Apple, Banana, Grape, Orange]

val fruits: List<String> = listOf("Apple", "Banana", "Grape")
val noBanana = fruits - "Banana" 

println(noBanana) //[Apple, Grape]

물론 이것도 처음부터 mutable한 리스트를 생성하면 add() 메소드 사용이 가능하다. 하지만 코틀린은 당연히 immutable 리스트를 권장함.

val fruits: MutableList<String> = mutableListOf("Apple", "Banana", "Grape")
fruits.add("Orange")
println(fruits) // [Apple, Banana, Grape, Orange]

Set은 정렬되지 않는 순서의 객체로 이루어진 컬렉션이다. setOf(), mutableSetOf(), hashSetOF() 함수를 사용해 생성이 가능하다.

val fruits: Set<String> = setOf("Apple", "Banana", "Apple")

Using Map

Map은 Key, Value 페어로 이루어진 컬렉션이다. mutableMapOf(), hashMapOF(), linkedMapOf(), sortedMapOF()등의 함수를 사용하여 생성이 가능하다.

val sites = mapOf("pragprog" to "https://www.pragprog.com",
  "agiledeveloper" to "https://agiledeveloper.com")

println(sites.size) //2
println(sites.containsKey("agiledeveloper")) //true
println(sites.containsValue("http://www.example.com")) //false
println(sites.contains("agiledeveloper")) //true
println("agiledeveloper" in sites) //true

[key] 연산자를 통해 값에 대한 접근이 가능하다. 만약 key에 대한 존재여부가 확실하지 않는다면 getOrDefault() 함수를 사용해 key 값이 존재하지 않을때에 대한 return 값을 지정해줄 수 있다.

val pragProgSite2: String? = sites["pragprog"]
val agiledeveloper =
  sites.getOrDefault("agiledeveloper", "http://www.example.com")

리스트와 똑같이 +,- 연산자를 사용해 추가 & 삭제된 값이 반영된 새로운 map을 생성해줄 수 있다.

val sitesWithExample = sites + ("example" to "http://www.example.com")
val withoutAgileDeveloper = sites - "agiledeveloper"

destructuring을 사용해 반복문을 돌면서 key, value값을 가져올 수 있다.

for ((key, value) in sites) {
    println("$key --- $value")
}
profile
주니어 개발쟈🤦‍♂️

0개의 댓글