Kotlin 공식 Example로 공부하기 - Collections#1(List, Set, Map, filter, map, any, all, none, first, last, lastOrNull,firstOrNull, find, findLast, count)
Collections (1)
- 저는 전문 번역가도 아니고, 의역을 넘어 오역, 심지어 그냥 제가 읽고 싶은대로 읽은 내용이 있을 수 있습니다.
- 개인 공부를 한 것을 포스트로 남기고 있으며 틀린 부분이 있으면 지적해주시면 수정하도록 하겠습니다.
- 원문 : https://play.kotlinlang.org/byExample/05_Collections/01_List
- Collections 부분은 꽤 길어서 나누어 작성했습니다.
List
리스트(List)는 순서가 있는 아이템의 컬렉션입니다.
코틀린에서 리스트는 둘 중에 하나가 될 수 있습니다. (mutable(MutableList
), read-only(List
))
리스트를 생성하기 위해서 표준 라이브러리 함수를 사용합니다.
listOf
: 읽기 전용 리스트mutableListOf
: 가변 리스트
원치 않는 수정을 방지하기 위해서 가변 리스트를 List
로 캐스팅해서 가변 리스트의 읽기 전용 뷰를 얻습니다.
val systemUsers: MutableList<Int> = mutableListOf(1, 2, 3) // 1
val sudoers: List<Int> = systemUsers // 2
fun addSudoer(newUser: Int) { // 3
systemUsers.add(newUser)
}
fun getSysSudoers(): List<Int> { // 4
return sudoers
}
fun main() {
addSudoer(4) // 5
println("Tot sudoers: ${getSysSudoers().size}") // 6
getSysSudoers().forEach { // 7
i -> println("Some useful info on user $i")
}
// getSysSudoers().add(5) <- Error! // 8
}
mutableListOf
를 이용하여MutableList
를 생성합니다.- 리스트의 읽기 전용 뷰를 생성합니다.
MutableList
에 아이템을 추가합니다.- 불변(Immutable)의
List
를 리턴하는 함수입니다. MutableList
를 업데이트합니다. 관련된 모든 읽기 전용 뷰도 같은 객체를 가리키므로 같이 업데이트 됩니다.- 읽기 전용 리스트의 사이즈를 조회합니다.
- 리스트를 반복하고 리스트의 요소를 출력합니다.
- 읽기 전용 뷰에 쓰기를 시도하면 컴파일 에러가 발생합니다.
Set
set
은 중복을 지원하지 않는 순서가 없는 컬렉션입니다.
set
을 생성하기 위한 함수 setOf()
와 mutableSetOf()
가 있습니다.
가변(mutable) set의 읽기 전용 뷰는 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
을 생성합니다.- 요소가 실제로 추가되었는지를 나타내는 boolean 값을 리턴합니다.
- 함수의 인자를 기준으로 문자열을 리턴합니다.
- 성공 메시지 출력 :
set
에 새로운 요소가 추가되었습니다. - 실패 메시지 출력 : 이미 존재하는 요소와 중복되기 때문에 이 요소는 추가되지 않았습니다.
Map
map
은 key/value 쌍을 갖는 컬렉션입니다. (key는 유일하며 일치하는 값을 조회하는데 사용됩니다.)
map
을 생성하기 위한 함수 mapOf()
와 mutableMapOf()
가 있습니다.
가변(mutable) map의 읽기 전용 뷰는 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
}
- 가변(mutable)
Map
을 생성합니다. Map
의 읽기 전용 뷰를 생성합니다.Map
의 key가 존재하는지 검사합니다.- 일치하는 값을 불러오고 상수 값으로 증가시킵니다.
- 불변(immutable)의
Map
을 순회하고 key/value 쌍을 출력합니다. - 업데이트 전에 계좌 포인트를 읽습니다.
- 존재하는 계좌를 두 번 업데이트합니다. (updatePointsCredit(1) 두 번 호출, Map에서는 1이라는 key를 갖는 계정이 있음)
- 존재하지 않는 계좌를 업데이트하려고 시도합니다 : 에러 메시지가 출력됩니다.
- 업데이트 후에 계좌 포인트를 읽습니다.
filter(collection method)
filter
함수는 컬렉션을 필터링할 수 있는 함수입니다.
filter
함수는 람다로 필터 조건 인자(predicate)를 받습니다.
필터 조건 인자(predicate)는 각 요소에 적용됩니다.
필터 조건 인자(predicate)를 true
로 만드는 요소는 결과 컬렉션에 요소로 리턴됩니다.
val numbers = listOf(1, -2, 3, -4, 5, -6) // 숫자 컬렉션(리스트) 정의
val positives = numbers.filter { x -> x > 0 } // 양수(positives)를 얻습니다
val negatives = numbers.filter { it < 0 } // 음수(negatives)를 얻기 위해 'it' 축약 표현법을 사용합니다.
map(collection method)
map
확장 함수를 사용하면 컬렉션의 모든 요소에 변환
기능을 적용할 수 있습니다. 변환
기능은 람다로 받습니다.
val numbers = listOf(1, -2, 3, -4, 5, -6) // 숫자 컬렉션(리스트) 정의
val doubled = numbers.map { x -> x * 2 } // 모든 요소를 2배로 만듭니다.
val tripled = numbers.map { it * 3 } // 'it' 축약 표현법을 사용하여 모든 요소를 3배로 만듭니다.
any, all, none(collection method)
이 컬렉션 메소드들은 주어진 조건(predicated)에 일치하는 요소가 존재하는지 검사하는 메소드입니다.
any
컬렉션에 주어진 조건(predicated)을 만족하는 요소가 하나 이상이 있다면 true
를 리턴합니다.
val numbers = listOf(1, -2, 3, -4, 5, -6) // 숫자 컬렉션 정의
val anyNegative = numbers.any { it < 0 } // 음수가 존재하는지 체크 = true
val anyGT6 = numbers.any { it > 6 } // 6보다 큰 요소가 있는지 체크 = false
all
컬렉션에 주어진 조건(predicated)을 모든 요소가 만족해야만 true
를 리턴합니다.
val numbers = listOf(1, -2, 3, -4, 5, -6) // 숫자 컬렉션 정의
val allEven = numbers.all { it % 2 == 0 } // 모든 요소가 짝수인지 체크 = false
val allLess6 = numbers.all { it < 6 } // 모든 요소가 6보다 작은지 체크 = true
none
컬렉션에 주어진 조건(predicated)을 만족하는 요소가 하나도 없어야 true
를 리턴합니다. (= 모든 요소가 조건을 다 만족하지 않아야 합니다)
val numbers = listOf(1, -2, 3, -4, 5, -6) // 숫자 컬렉션 정의
val allEven = numbers.none { it % 2 == 1 } // 홀수가 없는지 체크(= 모두 짝수) = false
val allLess6 = numbers.none { it > 6 } // 6보다 큰 요소가 없는지 체크 = true
find, findLast (collection method)
find
와 findLast
함수는 주어진 조건(predicated)을 만족하는 요소 중 첫번째 혹은 마지막 요소를 리턴합니다. 만약 주어진 조건(predicated)을 만족하는 요소가 하나도 없으면 null
을 리턴합니다.
val words = listOf("Lets", "find", "something", "in", "collection", "somehow")
// 문자열 컬렉션 정의
val first = words.find { it.startsWith("some") } // "some"으로 시작하는 첫 번째 요소 찾기(=something)
val last = words.findLast { it.startsWith("some") } // "some"으로 시작하는 마지막 요소 찾기(=somehow)
val nothing = words.find { it.contains("nothing") } // "nothing"을 포함하는 첫 번째 요소 찾기(=null)
first, last
이 함수들은 첫 번째, 마지막 요소를 리턴합니다.
조건(predicated)과 함께 이 함수들을 사용할 수도 있습니다. 이 경우 주어진 조건(predicated)에 일치하는 요소 중 첫 번째, 마지막 요소를 리턴합니다.
만약 컬렉션이 비어있거나(empty) 주어진 조건에 일치하는 요소가 없으면 NoSuchElementException
을 발생시킵니다. (predicated를 적용하면, find
, findLast
랑 같은 역할을 수행하지만 주어진 조건을 만족하지 않으면 null
을 리턴하느냐, NoSuchElementException
을 발생시키느냐의 차이가 있습니다.)
val numbers = listOf(1, -2, 3, -4, 5, -6) // 숫자 컬렉션 정의
val first = numbers.first() // 첫번째 요소 찾기(= 1)
val last = numbers.last() // 마지막 요소 찾기(=-6)
val firstEven = numbers.first { it % 2 == 0 } // 짝수중 첫번째 요소 찾기(=-2)
val lastOdd = numbers.last { it % 2 != 0 } // 짝수가 아닌 요소 중 마지막 요소 찾기(=5)
firstOrNull
, lastOrNull
이 함수들은 first
와 last
함수와 거의 동일하게 동작하지만 하나의 차이가 있습니다 : 주어진 조건(predicated)에 일치하는 게 없는 경우 null
리턴
val words = listOf("foo", "bar", "baz", "faz") // 문자열 컬렉션 정의
val empty = emptyList<String>() // 빈 컬렉션 정의
val first = empty.firstOrNull() // 빈 컬렉션에서 첫번째 요소를 찾습니다 (=null)
val last = empty.lastOrNull() // 빈 컬렉션에서 마지막 요소를 찾습니다 (=null)
val firstF = words.firstOrNull { it.startsWith('f') } // 'f'로 시작하는 첫번째 요소(=foo)
val firstZ = words.firstOrNull { it.startsWith('z') } // 'z'로 시작하는 첫번째 요소(=null)
val lastF = words.lastOrNull { it.endsWith('f') } // 'f'로 끝나는 마지막 요소(=null)
val lastZ = words.lastOrNull { it.endsWith('z') } // 'z'로 끝나는 마지막 요소(=faz)
count (collection method)
count 함수는 컬렉션 요소의 총 개수 또는 주어진 조건(predicated)에 일치하는 요소의 총 개수를 리턴합니다.
val numbers = listOf(1, -2, 3, -4, 5, -6) // 숫자 컬렉션 정의
val totalCount = numbers.count() // 컬렉션 요소의 총 개수 리턴(=6)
val evenCount = numbers.count { it % 2 == 0 } // 짝수인 요소의 총 개수 리턴(=3)
이어지는 컬렉션 내용은 다음 포스트에서 계속 작성할 예정입니다.
컬렉션은 프로그래밍에서 많이 사용하는 List, Set, Map으로 이뤄져있고 컬렉션에서 사용하기 좋은 함수들이 별도로 많이 정의되어 있는 것을 확인할 수 있었습니다.
특히나 자바에서는 필터링한 컬렉션 요소의 개수를 구할 때, filter(predicated)로 필터링을 한 후에 count()로 스트림API를 두 번 써야했는데 코틀린에서는 count(predicated) 함수가 있어서 한 번에 처리할 수 있어서 좋은 것 같습니다.