일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- Flutter
- 함수
- text
- Class
- crawler
- variable
- animation
- Collection
- kotlin
- python
- 다트
- 콜렉션
- import
- function
- 플러터
- 웹크롤러
- textstyle
- set
- map
- 크롤러
- Android
- Yocto
- 코틀린
- pushnamed
- List
- DART
- ML
- 파이썬
- package
- 클래스
- Today
- Total
조용한 담장
코틀린(Kotlin) Collections : Constructing Collections 본문
코틀린(kotlin) 의 collection (set, list, map) 을 살펴보자.
원문 https://kotlinlang.org/docs/reference/collections-overview.html, https://kotlinlang.org/docs/reference/constructing-collections.html 을 보며 정리.
Collection Types
코틀린은 standard library 를 통해 collection type 인 set, list, map 세개를 제공한다.
크게 두개의 인터페이스가 있다.
- 읽기 전용(read-only) 인터페이스 : collection element 에 접근하기 위한 기능을 제공
- 변경 가능(mutable) 인터페이스 : collection element 를 더하거나, 지우거나, 변경하는 등의 기능을 제공
mutable collection 을 사용하기 위해서 var 를 사용해야만 하는것은 아니다. 변수가 참조하는 collection 이 쓰기 동작에 의해 변경되는 것이기 때문에 참조자인 변수가 변경되는 것이 아니기 때문이다.
val numbers = mutableListOf("one", "two", "three", "four")
numbers.add("five") // this is OK
//numbers = mutableListOf("six", "seven") // compilation error
covariant read-only collection types
read-only collection type 들은 covariant 하다. 즉, Rectangle class 가 Shape 의 상속자 관계라면, List<Rectangle> 과 List<Shape> 의 관계도 동일하게 된다.
정리하자면, collection type 들은 element 의 type 에 따라서 subtype 관계(subtyping relationship) 를 가지게 된다.
Map 은 key type 이 아닌 value type 에 따라 covariant 하게 된다.
반대로, mutable collection 은 covariant 하지 않는데, 이 특성은 가끔 런타임 오류를 발생시키는 요인이 된다.
mutable collection 이 covariant 하다고 가정하고, MutableList<Rectangle> 이 MutableList<Shape> 의 subtype 이라고 한다면, Shape 의 다른 상속자인 Circle 같은 클래스를 Shape 을 대신하여 사용할 수 있겠지만, 이는 결국 Rectangle type 이 아닌 문제를 가지게 된다.
collection interface 의 다이어그램
Collections
Collection<T> 는 collection 의 관계도 최상위에 위치한다. read-only collection 의 공통적인 동작인(retrieving size, checking item membership, ...) 가진 인터페이스 이다.
Collection 은 Iterable<T> 인터페이스를 상속하여 iterating element 동작을 수행할 수 있다.
아래의 예제처럼 Collection 을 그 상속자들 (List, Set) 타입을 대신하여 사용할 수도 있다.
fun printAll(strings: Collection<String>) {
for(s in strings) print("$s ")
println()
}
fun main() {
val stringList = listOf("one", "two", "one")
printAll(stringList)
val stringSet = setOf("one", "two", "three")
printAll(stringSet)
}
// output:
// one two one
// one two three
MutableCollection 은 add, remove 같은 쓰기 동작을 가진 Collection 이다.
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)
}
// output:
// [ago, in, far, far]
List
List<T> : 지정된 순서대로 element 저장, 0 부터 시작하는 인덱스로 element 접근.
val numbers = listOf("one", "two", "three", "four")
println("Number of elements: ${numbers.size}")
println("Third element: ${numbers.get(2)}")
println("Fourth element: ${numbers[3]}")
println("Index of element \"two\" ${numbers.indexOf("two")}")
// output:
// Number of elements: 4
// Third element: three
// Fourth element: four
// Index of element "two" 1
길이가 같고 구조적으로 동일(structural equality) 하면 동등한 list 로 판단한다.
data class Person(var name: String, var age: Int)
fun main() {
val bob = Person("Bob", 31)
val people = listOf<Person>(Person("Adam", 20), bob, bob)
val people2 = listOf<Person>(Person("Adam", 20), Person("Bob", 31), bob)
println(people == people2)
bob.age = 32
println(people == people2)
}
// output:
// true
// false
val numbers = mutableListOf(1, 2, 3, 4)
numbers.add(5)
numbers.removeAt(1)
numbers[0] = 0
numbers.shuffle()
println(numbers)
// output:
// [0, 3, 4, 5]
List 의 기본 구현은 ArrayList 이다.
Set
Set<T> : 순서 없는 element 저장, 모든 element 는 유일함. null 도 유일하므로 한개만 가질 수 있다.
길이가 같고 각 element 가 같은 두개의 set 는 동등한 것으로 판단한다.
val numbers = setOf(1, 2, 3, 4)
println("Number of elements: ${numbers.size}")
if (numbers.contains(1)) println("1 is in the set")
val numbersBackwards = setOf(4, 3, 2, 1)
println("The sets are equal: ${numbers == numbersBackwards}")
// output:
// Number of elements: 4
// 1 is in the set
// The sets are equal: true
MutableSet 은 MutableCollection 로 부터 상속되어 쓰기 기능을 가진 Set 이다.
Set 은 LinkedHashSet 으로 기본 구현되기 때문에 first(), list() 같이 순서대로 접근이 가능하다.
val numbers = setOf(1, 2, 3, 4) // LinkedHashSet is the default implementation
val numbersBackwards = setOf(4, 3, 2, 1)
println(numbers.first() == numbersBackwards.first())
println(numbers.first() == numbersBackwards.last())
// output:
// false
// true
HashSet 으로 구현하면 순서는 의미없다. 메모리를 적게 먹는 장점이 있다.
Map
Map<K, V> : Collection 인터페이스의 상속자는 아니지만 kotlin collection type 으로 여겨진다. key-value pairs 저장. key 는 유일, 같은 value 를 가진 key 존재 가능.
val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key4" to 1)
println("All keys: ${numbersMap.keys}")
println("All values: ${numbersMap.values}")
if ("key2" in numbersMap) println("Value by key \"key2\": ${numbersMap["key2"]}")
if (1 in numbersMap.values) println("The value 1 is in the map")
if (numbersMap.containsValue(1)) println("The value 1 is in the map") // same as previous
// output:
// All keys: [key1, key2, key3, key4]
// All values: [1, 2, 3, 1]
// Value by key "key2": 2
// The value 1 is in the map
// The value 1 is in the map
순서 상관 없이 같은 key-value 값들을 가지면 동등한 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}")
// output:
// The maps are equal: true
MutableMap 은 쓰기 기능을 가진 Map 이다.
val numbersMap = mutableMapOf("one" to 1, "two" to 2)
numbersMap.put("three", 3)
numbersMap["one"] = 11
println(numbersMap)
// output:
// {one=11, two=2, three=3}
map 은 LinkedHashMap 으로 기본 구현되기 때문에 element 삽입 시 순서가 지정된다.
HashMap 으로 구현하면 순서는 의미없다.
Constructing from elements
일반적인 collection 생성은 아래의 함수들을 사용한다.
listOf<T>(), setOf<T>(), mutableListOf<T>(), mutableSetOf<T>()
colleciton element 는 argument 에 , 로 구분하여 넣는다.
val numbersSet = setOf("one", "two", "three", "four")
val emptySet = mutableSetOf<String>()
map 은 mapOf() 와 mutableMapOf() 를 사용한다.
argument 는 Pair object 로 전달되며 주로 to 를 쓰는 infix function 을 사용한다.
val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key4" to 1)
to 는 Pair object 를 만드는 일을 하기 때문에 성능이 중요한 경우에는 좋지 않을 수 있다.
대안으로, 아래의 예제처럼 collection 을 생성하고 apply() 같은 쓰기 기능의 함수을 사용하는 방법이 있다.
val numbersMap = mutableMapOf<String, String>().apply { this["one"] = "1"; this["two"] = "2" }
Empty collections
element 가 없는 collection 을 만드는 함수가 있다. element 의 type 을 명시해야 한다.
emptyList(), emptySet(), emptyMap()
val empty = emptyList<String>()
Initializer functions for lists
list 의 생성자 중에 list 의 크기와 인덱스 기반으로 값을 정의하는 초기화 함수(initializer funciton) 을 가지는 것이 있다.
val doubled = List(3, { it * 2 }) // or MutableList if you want to change its content later
println(doubled)
// output:
// [0, 2, 4]
Concrete type constructors
ArrayList 나 LinkedList 같은 concrete type collection 은 따로 정의된 생성자(constructor)들을 사용하여 만들 수 있다.
Set 과 Map 의 구현에 사용할 수 있는 비슷한 생성자들이 있다.
val linkedList = LinkedList<String>(listOf("one", "two", "three"))
val presizedSet = HashSet<Int>(32)
Copying
collection 의 복사는 toList(), toMutableList(), toSet() 같은 함수를 사용한다.
val sourceList = mutableListOf(1, 2, 3)
val copyList = sourceList.toMutableList()
val readOnlyCopyList = sourceList.toList()
sourceList.add(4)
println("Source size: ${sourceList.size}")
println("Copy size: ${copyList.size}")
//readOnlyCopyList.add(4) // compilation error
println("Read-only copy size: ${readOnlyCopyList.size}")
// output:
// Source size: 4
// Copy size: 3
// Read-only copy size: 3
이런 함수들은 collection 간의 type 변환에도 사용될 수 있다.
val sourceList = mutableListOf(1, 2, 3)
val copySet = sourceList.toMutableSet()
copySet.add(3)
copySet.add(4)
println(copySet)
// output:
// [1, 2, 3, 4]
collection 의 element 복사 대신에 인스턴스의 새로운 레퍼런스를 생성하는 방법이 있다.
새로운 레퍼런스는 이미 존재하는 collection 변수로 초기화 할 때 생성되고, 그 collection 인스턴스가 레퍼런스를 통해 변경되면 연관된 모든 레퍼런스에 반영된다.
val sourceList = mutableListOf(1, 2, 3)
val referenceList = sourceList
referenceList.add(4)
println("Source size: ${sourceList.size}")
// output:
// Source size: 4
collection 초기화는 가변성을 제한(restricting mutability) 하기 위한 용도로 사용될 수 있다.
예를들어, MutableList 를 참조하는 List 를 생성하면 컴파일러는 이 레퍼런스를 통해 collection 을 수정하는 행위에 에러를 발생시킨다.
val sourceList = mutableListOf(1, 2, 3)
val referenceList: List<Int> = sourceList
//referenceList.add(4) //compilation error
sourceList.add(4)
println(referenceList) // shows the current state of sourceList
// output:
// [1, 2, 3, 4]
Invoking functions on other collections
다른 collection 의 다양한 동작의 결과로써 collection 이 생성될 수 있다.
val numbers = listOf("one", "two", "three", "four")
val longerThan3 = numbers.filter { it.length > 3 }
println(longerThan3)
// output:
// [three, four]
val numbers = setOf(1, 2, 3)
println(numbers.map { it * 3 })
println(numbers.mapIndexed { idx, value -> value * idx })
// output:
// [3, 6, 9]
// [0, 2, 6]
val numbers = listOf("one", "two", "three", "four")
println(numbers.associateWith { it.length })
// output:
// {one=3, two=3, three=5, four=4}
'kotlin' 카테고리의 다른 글
코틀린(Kotlin) Collections : Ranges and Progressions (0) | 2020.01.02 |
---|---|
코틀린(Kotlin) Collections : Iterators (0) | 2020.01.02 |
코틀린(Kotlin) : 인라인 함수 (Inline Functions) (0) | 2020.01.02 |
코틀린(Kotlin) : 고차함수 와 람다 (Higher-Order Functions and Lambdas) (0) | 2020.01.02 |
코틀린(Kotlin) : 함수(Function) (0) | 2020.01.02 |