2022-03-10 작성완료
list는 아이템의 순서 있는 컬렉션이다(ordered collection). 코틀린에서 리스트는 가변(mutable)이거나 (MutableList
) 읽기 전용(List
)일 수 있다. 표준 라이브러리 함수 listOf()
를 읽기 전용 리스트 생성에 사용하고 mutableListOf()
를 가변 리스트 생성에 사용한다. 원하지 않는 수정을 피하기 위해 가변 리스트를 List
로 캐스팅해 읽기 전용 뷰를 만들 수 있다.
val systemUsers: MutableList<Int> = mutableListOf(1, 2, 3) // 1
val sudoers: List<Int> = systemUsers // 2
fun addSystemUser(newUser: Int) { // 3
systemUsers.add(newUser)
}
fun getSysSudoers(): List<Int> { // 4
return sudoers
}
fun main() {
addSystemUser(4) // 5
println("Tot sudoers: ${getSysSudoers().size}") // 6
getSysSudoers().forEach { // 7
i -> println("Some useful info on user $i")
}
// getSysSudoers().add(5) <- Error! // 8
}
MutableList
를 생성한다.MutableList
에 더한다.List
를 리턴하는 함수MutableList
를 업데이트한다. 모든 관련된 읽기 전용 뷰 또한 업데이트된다. 동일한 객체를 가리키고 있기 때문이다.set은 순서가 없는 컬렉션이고 중복(duplicates)를 지원하지 않는다. set을 생성하기 위해서는 setOf()
와 mutableSetOf()
함수를 사용한다. 가변 셋의 읽기 전용 뷰는 그것을 Set
으로 캐스팅해서 얻을 수 있다.
val openIssues: MutableSet<String> = mutableSetOf("uniqueDescr1", "uniqueDescr2", "uniqueDescr3") // 1
fun addIssue(uniqueDesc: String): Boolean {
return openIssues.add(uniqueDesc) // 2
}
fun getStatusLog(isAdded: Boolean): String {
return if (isAdded) "registered correctly." else "marked as duplicate and rejected." // 3
}
fun main() {
val aNewIssue: String = "uniqueDescr4"
val anIssueAlreadyIn: String = "uniqueDescr2"
println("Issue $aNewIssue ${getStatusLog(addIssue(aNewIssue))}") // 4
println("Issue $anIssueAlreadyIn ${getStatusLog(addIssue(anIssueAlreadyIn))}") // 5
}
set
을 생성한다.Set
에 추가되었다.map은 키/값 쌍의 컬렉션이다. 각 키는 유니크하고 상응하는 값을 인출하기 위해 사용된다. 맵 생성에는 mapOf()
와 mutableMapOf()
함수를 사용한다. to infix function은 초기화를 덜 noisy하게 만든다. 가변 맵의 읽기 전용 뷰는 Map
으로 캐스팅해 얻을 수 있다.
const val POINTS_X_PASS: Int = 15
val EZPassAccounts: MutableMap<Int, Int> = mutableMapOf(1 to 100, 2 to 100, 3 to 100) // 1
val EZPassReport: Map<Int, Int> = EZPassAccounts // 2
fun updatePointsCredit(accountId: Int) {
if (EZPassAccounts.containsKey(accountId)) { // 3
println("Updating $accountId...")
EZPassAccounts[accountId] = EZPassAccounts.getValue(accountId) + POINTS_X_PASS // 4
} else {
println("Error: Trying to update a non-existing account (id: $accountId)")
}
}
fun accountsReport() {
println("EZ-Pass report:")
EZPassReport.forEach { // 5
k, v -> println("ID $k: credit $v")
}
}
fun main() {
accountsReport() // 6
updatePointsCredit(1) // 7
updatePointsCredit(1)
updatePointsCredit(5) // 8
accountsReport() // 9
}
Map
을 생성한다.Map
의 읽기 전용 뷰를 생성한다. Map
의 키가 존재하는지를 체크한다.Map
을 순회하고 키/값 페어를 프린트한다.filter
함수는 컬렉션을 필터링한다. 필터 술부를 람다 파라미터로 가진다. 술부는 각 요소에 대해 적용된다. 결과 배열에는 술부를 true
로 만드는 요소가 반환된다.
val numbers = listOf(1, -2, 3, -4, 5, -6) // 1
val positives = numbers.filter { x -> x > 0 } // 2
val negatives = numbers.filter { it < 0 } // 3
it
표기법을 사용한다.map
표현식 함수는 컬렉션의 모든 요소에 변환을 적용한다. 이는 변환함수를 람다 파라미터로 가진다.
val numbers = listOf(1, -2, 3, -4, 5, -6) // 1
val doubled = numbers.map { x -> x * 2 } // 2
val tripled = numbers.map { it * 3 } // 3
it
표현식을 사용한다.이 세 가지는 주어진 술부와 일치하는 컬렉션 요소가 있는지 체크한다.
any
any
는 하나 이상의 요소가 주어진 술부와 일치할 경우 true
를 반환한다.
val numbers = listOf(1, -2, 3, -4, 5, -6) // 1
val anyNegative = numbers.any { it < 0 } // 2
val anyGT6 = numbers.any { it > 6 } // 3
all
all
은 컬렉션의 모든 요소가 주어진 술부와 일치할 경우 true
를 반환한다.
val numbers = listOf(1, -2, 3, -4, 5, -6) // 1
val allEven = numbers.all { it % 2 == 0 } // 2
val allLess6 = numbers.all { it < 6 } // 3
none
none
은 컬렉션의 어떤 요소도 주어진 술부와 일치하지 않을 때 true
를 반환한다.
val numbers = listOf(1, -2, 3, -4, 5, -6) // 1
val allEven = numbers.none { it % 2 == 1 } // 2
val allLess6 = numbers.none { it > 6 } // 3
find
와 findLast
함수는 주어진 술부와 일치하는 첫 번째 또는 마지막 컬렉션 요소를 반환한다. 일치하는 요소가 없다면 null
을 반환한다.
val words = listOf("Lets", "find", "something", "in", "collection", "somehow") // 1
val first = words.find { it.startsWith("some") } // 2
val last = words.findLast { it.startsWith("some") } // 3
val nothing = words.find { it.contains("nothing") } // 4
first
, last
이 함수들은 컬렉션의 처음과 마지막 요소를 반환한다. 술부와 함께 이를 사용할 수도 있다; 이 경우에는 주어진 술부와 일치하는 처음 또는 마지막 요소가 반환된다.
만약 컬렉션이 비었거나 해당 술부와 일치하는 요소를 가지고 있지 않다면 NoSuchElementException
을 반환한다.
val numbers = listOf(1, -2, 3, -4, 5, -6) // 1
val first = numbers.first() // 2
val last = numbers.last() // 3
val firstEven = numbers.first { it % 2 == 0 } // 4
val lastOdd = numbers.last { it % 2 != 0} // 5
firstOrNull
, lastOrNull
이 함수들은 한 가지 차이점을 빼면 위와 거의 동일하게 동작한다: 일치하는 요소가 없을 경우 null
을 반환한다.
val words = listOf("foo", "bar", "baz", "faz") // 1
val empty = emptyList<String>() // 2
val first = empty.firstOrNull() // 3
val last = empty.lastOrNull() // 4
val firstF = words.firstOrNull { it.startsWith('f') } // 5
val firstZ = words.firstOrNull { it.startsWith('z') } // 6
val lastF = words.lastOrNull { it.endsWith('f') } // 7
val lastZ = words.lastOrNull { it.endsWith('z') } // 8
null
이 되어야 한다.null
이 될 것이다.count
함수는 컬렉션 요소의 총 수 또는 주어진 술부와 일치하는 요소의 수를 반환한다.
val numbers = listOf(1, -2, 3, -4, 5, -6) // 1
val totalCount = numbers.count() // 2
val evenCount = numbers.count { it % 2 == 0 } // 3
함수 associateBy
와 groupBy
는 지정된 키로 인덱싱된 컬렉션의 요소들로부터 맵을 빌드한다. 키는 keySelector
파라미터에 정의된다. 또한 선택적인 valueSelector
를 지정해 맵 요소의 value
에 무엇이 저장될지를 지정할 수 있다.
associateBy
와 groupBy
의 차이점은 동일한 키를 가지고 객체를 처리하는 방법이다:
associateBy
는 마지막 적합 요소 (last suitable element)를 값으로 사용한다.groupBy
는 모든 적합한 요소들의 리스트를 빌드하고 그것을 값에 집어넣는다.반환된 맵은 원 컬렉션의 entry iteration order를 유지한다.
data class Person(val name: String, val city: String, val phone: String) // 1
val people = listOf( // 2
Person("John", "Boston", "+1-888-123456"),
Person("Sarah", "Munich", "+49-777-789123"),
Person("Svyatoslav", "Saint-Petersburg", "+7-999-456789"),
Person("Vasilisa", "Saint-Petersburg", "+7-999-123456"))
val phoneBook = people.associateBy { it.phone } // 3
val cityBook = people.associateBy(Person::phone, Person::city) // 4
val peopleCities = people.groupBy(Person::city, Person::name) // 5
val lastPersonCity = people.associateBy(Person::city, Person::name) // 6
it.phone
이 keySelector
이고, valueSelector
는 정의되지 않았으므로 Person
객체가 맵의 값이 된다. Person::city
는 valueSelector
이므로 맵의 값에는 도시만이 담기게 된다.partition
함수는 원본 컬렉션을 주어진 술부를 사용해 다음과 같은 리스트 쌍으로 분리한다:
true
인 요소들false
인 요소들val numbers = listOf(1, -2, 3, -4, 5, -6) // 1
val evenOdd = numbers.partition { it % 2 == 0 } // 2
val (positives, negatives) = numbers.partition { it > 0 } // 3
numbers
를 짝수와 홀수 리스트 Pair
로 나눈다.numbers
를 양수와 음수 리스트로 나눈다. Pair의 멤버들을 얻기 위해 Pair 구조 분해(Pair destructuring)가 이루어졌다.flatMap
은 컬렉션의 각 요소를 iterable 객체로 변환하다. 그리고 변환 결과의 단일 리스트를 만든다. 변환은 유저가 정의한다.
val fruitsBag = listOf("apple","orange","banana","grapes") // 1
val clothesBag = listOf("shirts","pants","jeans") // 2
val cart = listOf(fruitsBag, clothesBag) // 3
val mapBag = cart.map { it } // 4
val flatMapBag = cart.flatMap { it } // 5
fruitsBag
과 clothesBag
을 cart
리스트에 넣는다.cart
요소들의 map
을 만든다. 이것은 두 리스트의 리스트(두 리스트를 요소로 가지는)가 된다.cart
요소들의 flatMap
을 만든다. 이것은 두 리스트에서 온 요소들로 구성된 단일 리스트가 된다. minOrNull
과 maxOrNull
함수는 컬렉션의 최소/최대 요소를 반환한다. 컬렉션이 비어 있다면 null
을 반환한다.
val numbers = listOf(1, 2, 3)
val empty = emptyList<Int>()
val only = listOf(3)
println("Numbers: $numbers, min = ${numbers.minOrNull()} max = ${numbers.maxOrNull()}") // 1
println("Empty: $empty, min = ${empty.minOrNull()}, max = ${empty.maxOrNull()}") // 2
println("Only: $only, min = ${only.minOrNull()}, max = ${only.maxOrNull()}") // 3
null
을 반환한다.sorted
는 자연 정렬 순서(natural sort order) (오름차순)으로 정렬된 컬렉션 요소 리스트를 반환한다.
sortedBy
는 지정된 선택자 함수(selector function)에 의해 반환된 값의 자연 정렬 순서에 따라 요소를 정렬한다.
val shuffled = listOf(5, 4, 2, 1, 3, -10) // 1
val natural = shuffled.sorted() // 2
val inverted = shuffled.sortedBy { -it } // 3
val descending = shuffled.sortedDescending() // 4
val descendingBy = shuffled.sortedByDescending { abs(it) } // 5
-it
을 사용해 뒤집어진 자연 순서(inverted natural order)로 정렬한다.sortedDescending
을 사용해 뒤집어진 자연 순서로 정렬한다.abs(it)
을 사용해 아이템 절대값의 뒤집어진 자연 순서로 정렬한다. 맵에 적용하면 []
연산자는 주어진 키에 대응하는 값을 반환한다. 맵에 해당 키가 없다면 null
을 반환한다.
getValue
함수는 주어진 키로 대응하는 값을 반환한다. 키를 찾을 수 없다면 예외를 던진다. withDefault
로 생성된 맵에는 getValue
는 예외 대신 기본 값을 반환한다.
val map = mapOf("key" to 42)
val value1 = map["key"] // 1
val value2 = map["key2"] // 2
val value3: Int = map.getValue("key") // 1
val mapWithDefault = map.withDefault { k -> k.length }
val value4 = mapWithDefault.getValue("key2") // 3
try {
map.getValue("anotherKey") // 4
} catch (e: NoSuchElementException) {
println("Message: $e")
}
null
을 반환한다.NoSuchElementException
을 반환한다.zip
함수는 주어진 두 컬렉션을 새 컬렉션으로 병합한다(Merge). 기본적으로 결과 컬렉션은 동일한 인덱스로 소스 컬렉션 요소들의 Pair
를 가진다. 하지만 결과 컬렉션 요소 구조를 정의할 수 있다.
val A = listOf("a", "b", "c") // 1
val B = listOf(1, 2, 3, 4) // 1
val resultPairs = A zip B // 2
val resultReduce = A.zip(B) { a, b -> "$a$b" } // 3
getOrElse
는 컬렉션 요소에 대한 안전한 접근을 제공한다. 이것은 인덱스와, 인덱스가 out of bound일 경우 기본 값을 제공하는 함수를 가지고 있다.
val list = listOf(0, 10, 20)
println(list.getOrElse(1) { 42 }) // 1
println(list.getOrElse(10) { 42 }) // 2
1
에 있는 요소를 프린트한다.10
은 out of bounds이므로 42
를 프린트한다.getOrElse
는 주어진 키로 값을 얻기 위해 맵에도 사용할 수 있다.
val map = mutableMapOf<String, Int?>()
println(map.getOrElse("x") { 1 }) // 1
map["x"] = 3
println(map.getOrElse("x") { 1 }) // 2
map["x"] = null
println(map.getOrElse("x") { 1 }) // 3