https://play.kotlinlang.org/koans/Collections/Introduction/Task.kt
코틀린 공식문서에 있는 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]' 카테고리의 다른 글
코틀린[Kotlin] 컬렉션 정리 - 매핑 / 필터링 등(About the Collections in Kotlin - Mapping / Filtering etc..) (0) | 2022.05.13 |
---|---|
코틀린[Kotlin] 컬렉션 정리 - 정렬편(About the Collections in Kotlin - Sort) (0) | 2022.05.13 |
코틀린[Kotlin] 배열, 리스트에서 중복값 제거하기( distinct() ) (0) | 2022.04.27 |
코틀린[Kotlin] 괄호와 공백을 포함한 문자열을 리스트로 만들기 (0) | 2022.03.22 |
코틀린[Kotlin] 컬렉션 API : filter, map, all, any, count, find(firstOrNull) (0) | 2022.02.21 |
최근댓글