반응형

https://play.kotlinlang.org/koans/Collections/Introduction/Task.kt

 

Kotlin Playground: Edit, Run, Share Kotlin Code Online

 

play.kotlinlang.org

코틀린 공식문서에 있는 Colletions 파트를 읽으며 정리해보았습니다.


Introduction

코틀린은 자바와 섞어 쓰기 쉽다. 코틀린의 기본 컬렉션은 자바의 컬렉션과 아주 유사하다.

  • 코틀린 기본 라이브러리는 기본 컬렉션 타입으로 sets, lists, maps를 제공함.
    • 각 컬렉션 타입은 read-only / mutable 인터페이가 구현된 한 쌍으로 존재
    • read-only는 읽기 전용 mutable은 element 수정이 가능하다.

동일한 객체의 컬렉션 원소의 값을 수정하기 위해 mutableLis를 var로 선언하지 않아도 됨.

val numbers = mutableListOf("one", "two", "three", "four")
numbers.add("five")   // this is OK
println(numbers)
//numbers = mutableListOf("six", "seven")      // compilation error

read-only 타입의 컬렉션은 covariant 타입이다. 

Rectangle 클래스가 Shape 클래스를 상속받는 다면 List<Rectangle>을 List<Shape>어디서든 사용이 가능하다.

컬렉션 타입은 원소의 타입과 동일한 subtyping 관계를 가진다. Map의 value 또한 covariant 이지만 key는 아니다.

 

mutable 타입의 컬렉션은 covariant 타입이 아니다. 아래와 같은 이유로 런타임이 실패하기 때문.

MutableList<Rectangle>가 MutableList<Shape>의 subtype이라면 Circle과 같은 다른 Shape을 상속받은 객체를 MutableList<Rectangle>에 넣을 수 있게 됨.

코틀린 컬렉션 인터페이스의 다이어그램

 


Collection

Collection<T>

Collection<T>는 컬렉션의 최상위 계층이며 Iterable<T>를 상속받는다. 이 인터페이스는 개수 세기, 원소 확인과 같은 read-only 컬렉션을 대표한다.  Collection을 파라미터로 받게 되면 다른 컬렉션 타입의 컬렉션들도 받을 수 있다.

fun printAll(strings: Collection<String>) {
    for(s in strings) print("$s ")
    println()
}
    
fun main() {
    val stringList = listOf("one", "two", "one")
    printAll(stringList) // print => one two one 
    
    val stringSet = setOf("one", "two", "three")
    printAll(stringSet) // print => one two three 
}

MutableCollection<T>

MutableCollection<T>은 add와 remove같이 쓰기가 가능한 컬렉션이다.

fun List<String>.getShortWordsTo(shortWords: MutableList<String>, maxLength: Int) {
    this.filterTo(shortWords) { it.length <= maxLength }
    // throwing away the articles
    val articles = setOf("a", "A", "an", "An", "the", "The")
    shortWords -= articles
}

fun main() {
    val words = "A long time ago in a galaxy far far away".split(" ")
    val shortWords = mutableListOf<String>()
    words.getShortWordsTo(shortWords, 3)
    println(shortWords) // Print => [ago, in, far, far]
}

List

List<T>

List<T>는 원소들을 정렬하거나 인덱스를 활용하여 저장한다.

인덱스의 범위는 0 ~ list.size -1 까지

val numbers = listOf("one", "two", "three", "four")
println("Number of elements: ${numbers.size}") // print => Number of elements: 4
println("Third element: ${numbers.get(2)}") // print => Third element: three
println("Fourth element: ${numbers[3]}") // print => Fourth element: four
println("Index of element \"two\" ${numbers.indexOf("two")}") // print => Index of element "two" 1

list의 원소는 (null 포함) 중복이 가능. 리스트는 동일한 객체를 여러 개 포함할 수 있다. 두 개의 리스트가 같은 크기의 구조적으로 동일한 원소를 같은 위치에 가진다면 동일한 원소로 판단된다.

val bob = Person("Bob", 31)
val people = listOf(Person("Adam", 20), bob, bob)
val people2 = listOf(Person("Adam", 20), Person("Bob", 31), bob)
println(people == people2) // print => true
bob.age = 32
println(people == people2) // print => false

MutableList<T>

MutableList<T>는 list에 쓰기 작업이 추가된 List이다. add나 remove를 구체적인 위치에 대해서 할 수 있다.

val numbers = mutableListOf(1, 2, 3, 4)
numbers.add(5)
numbers.removeAt(1)
numbers[0] = 0
numbers.shuffle()
println(numbers) // print => [4, 0, 5, 3]

array와 list는 비슷해 보이지만 한 가지 중요한 차이가 있다. array는 크기가 고정되어 바꿀 수 없고 list는 크기를 조절할 수 있다.


Set

Set<T>

set<T>은 고유한 원소를 저장한다. 정렬 방식은 일반적으로 정해져 있지 않다. null 또한 고유한 원소이며 Set은 하나의 null만을 가질 수 있다. 두 개의 set이 동일한 크기, 동일한 원소를 가지고 있을 경우 같은 set으로 판단된다.

val numbers = setOf(1, 2, 3, 4)
println("Number of elements: ${numbers.size}") // print => Number of elements: 4
if (numbers.contains(1)) println("1 is in the set") // print => 1 is in the set

val numbersBackwards = setOf(4, 3, 2, 1)
println("The sets are equal: ${numbers == numbersBackwards}") // print => The sets are equal: true

MutableSet<T>

MutableSet은 Set에 쓰기 작업이 추가된 것이다. Set-LInkedHashSet의 기본 구현은 원소 삽입 순서를 유지한다. 이러한 이유로 first(), last()와 같은 정렬 기반 함수는 아래와 같은 결과를 도출한다.

val numbers = setOf(1, 2, 3, 4)  // LinkedHashSet is the default implementation
val numbersBackwards = setOf(4, 3, 2, 1)

println(numbers.first() == numbersBackwards.first()) // print => false
println(numbers.first() == numbersBackwards.last()) // print => true

HashSet은 요소의 순서를 신경 쓰지 않기 때문에 이러한 함수를 호출하면 예기치 못한 결과를 도출한다.


Map

Map<K, V>

Map<K, V> 는 Collection 인터페이스를 상속받지 않지만 이것은 코틀린 컬렉션 타입이다. Map은 key-value 쌍으로 저장한다. key는 고유하지만 서로 다른 key가동일한 value를 가리킬 수 있다.

val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key4" to 1)

println("All keys: ${numbersMap.keys}") // print => All keys: [key1, key2, key3, key4]
println("All values: ${numbersMap.values}") // print => All values: [1, 2, 3, 1]
if ("key2" in numbersMap) println("Value by key \"key2\": ${numbersMap["key2"]}") // print => Value by key "key2": 2
if (1 in numbersMap.values) println("The value 1 is in the map") // print => The value 1 is in the map
if (numbersMap.containsValue(1)) println("The value 1 is in the map") // same as previous // print => The value 1 is in the map

두 개의 맵이 쌍의 순서와 상관없이 같은 쌍의 값을 포함하고 있으면 동일한 map이라고 판단된다.

val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key4" to 1)    
val anotherMap = mapOf("key2" to 2, "key1" to 1, "key4" to 1, "key3" to 3)

println("The maps are equal: ${numbersMap == anotherMap}") // print => The maps are equal: true

MutableMap<K, V>

MutableMap<K, V> 는 쓰기 작업이 가능한 Map이다. 예를 들어 새로운 key-value쌍을 추가하거나 업데이트할 수 있다.

val numbersMap = mutableMapOf("one" to 1, "two" to 2)
numbersMap.put("three", 3)
numbersMap["one"] = 11

println(numbersMap) // print => {one=11, two=2, three=3}

Map - LinkedHashMap의 기본 구현은 map을 반복할 때 원소의 순서를 보존한다. HashMap은 원소의 순서를 보존하지 않는다.


Kotlin standard library는 Collections 작업을 더 편리하게 해주는 많은 확장 함수를 포함하고 있다. 

예를 들면 컬렉션의 형태를 다른 형태로 변환시켜주는 toSet(), toList 등이 있다.


Basic Code

Shop.kt 의 코드 구성

data class City(val name: String) {
    override fun toString() = name
}

data class Product(val name: String, val price: Double) {
    override fun toString() = "'$name' for $price"
}

data class Order(val products: List<Product>, val isDelivered: Boolean)

data class Customer(val name: String, val city: City, val orders: List<Order>) {
    override fun toString() = "$name from ${city.name}"
}

data class Shop(val name: String, val customers: List<Customer>)

문제

Shop.getSetOfCustomers() 확장 함수를 구현하여라. 

fun Shop.getSetOfCustomers(): Set<Customer> =

정답

fun Shop.getSetOfCustomers(): Set<Customer> = customers.toSet()

.toSet()을 사용하여 List<Customer>인 customers를 Set으로 형 변환시켜주는 확장 함수를 만들었다.

 

다음 글

코틀린[Kotlin] 컬렉션 정리 - 정렬 편(About the Collections in Kotlin - Sort)

 

코틀린[Kotlin] 컬렉션 정리 - 정렬편(About the Collections in Kotlin - Sort)

https://play.kotlinlang.org/koans/Collections/Introduction/Task.kt Kotlin Playground: Edit, Run, Share Kotlin Code Online play.kotlinlang.org 코틀린 공식문서에 있는 Colletions 파트를 읽으며 정리해보..

soopeach.tistory.com

 

반응형
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기