본문 바로가기

Kotlin/Play Kotlin Example

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
}
  1. mutableListOf 를 이용하여 MutableList를 생성합니다.
  2. 리스트의 읽기 전용 뷰를 생성합니다.
  3. MutableList 에 아이템을 추가합니다.
  4. 불변(Immutable)의 List 를 리턴하는 함수입니다.
  5. MutableList 를 업데이트합니다. 관련된 모든 읽기 전용 뷰도 같은 객체를 가리키므로 같이 업데이트 됩니다.
  6. 읽기 전용 리스트의 사이즈를 조회합니다.
  7. 리스트를 반복하고 리스트의 요소를 출력합니다.
  8. 읽기 전용 뷰에 쓰기를 시도하면 컴파일 에러가 발생합니다.

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 
}
  1. set을 생성합니다.
  2. 요소가 실제로 추가되었는지를 나타내는 boolean 값을 리턴합니다.
  3. 함수의 인자를 기준으로 문자열을 리턴합니다.
  4. 성공 메시지 출력 : set에 새로운 요소가 추가되었습니다.
  5. 실패 메시지 출력 : 이미 존재하는 요소와 중복되기 때문에 이 요소는 추가되지 않았습니다.

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
}
  1. 가변(mutable) Map 을 생성합니다.
  2. Map의 읽기 전용 뷰를 생성합니다.
  3. Map의 key가 존재하는지 검사합니다.
  4. 일치하는 값을 불러오고 상수 값으로 증가시킵니다.
  5. 불변(immutable)의 Map을 순회하고 key/value 쌍을 출력합니다.
  6. 업데이트 전에 계좌 포인트를 읽습니다.
  7. 존재하는 계좌를 두 번 업데이트합니다. (updatePointsCredit(1) 두 번 호출, Map에서는 1이라는 key를 갖는 계정이 있음)
  8. 존재하지 않는 계좌를 업데이트하려고 시도합니다 : 에러 메시지가 출력됩니다.
  9. 업데이트 후에 계좌 포인트를 읽습니다.

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)

findfindLast 함수는 주어진 조건(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 이 함수들은 firstlast 함수와 거의 동일하게 동작하지만 하나의 차이가 있습니다 : 주어진 조건(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) 함수가 있어서 한 번에 처리할 수 있어서 좋은 것 같습니다.

반응형