Kotlin의 컬렉션

1. 시작하기 전에

이 Codelab에서는 컬렉션, Kotlin의 람다 및 고차 함수에 관해 자세히 알아봅니다.

기본 요건

  • 이전 Codelab에 나온 Kotlin 개념을 기본적으로 이해해야 합니다.
  • Kotlin 플레이그라운드를 사용하여 Kotlin 프로그램을 만들고 수정하는 방법을 잘 알고 있어야 합니다.

학습할 내용

  • 집합과 맵을 포함하는 컬렉션을 사용하는 방법
  • 람다의 기본사항
  • 고차 함수의 기본사항

필요한 항목

2. 컬렉션에 관해 알아보기

컬렉션은 단어 목록이나 직원 기록 모음과 같은 관련 항목의 그룹입니다. 컬렉션의 항목은 순서가 지정될 수도 있고 지정되지 않을 수도 있으며, 고유할 수도 있고 고유하지 않을 수도 있습니다. 컬렉션의 한 유형인 목록에 관해서는 이미 알아봤습니다. 목록의 항목에는 순서가 있지만 항목이 고유할 필요는 없습니다.

Kotlin에서는 목록과 마찬가지로 변경 가능한 컬렉션과 변경 불가능한 컬렉션이 구분됩니다. Kotlin은 항목 추가 또는 삭제, 컬렉션 보기, 조작을 위한 여러 함수를 제공합니다.

목록 만들기

이 작업에서는 숫자 목록을 만들고 숫자를 정렬하는 방법을 살펴봅니다.

  1. Kotlin 플레이그라운드를 엽니다.
  2. 어떤 코드이든 다음 코드로 교체합니다.
fun main() {
    val numbers = listOf(0, 3, 8, 4, 0, 5, 5, 8, 9, 2)
    println("list:   ${numbers}")
}
  1. 녹색 화살표를 탭하여 프로그램을 실행하고 나타나는 결과를 살펴봅니다.
list:   [0, 3, 8, 4, 0, 5, 5, 8, 9, 2]
  1. 목록에는 0에서 9까지 숫자 10개가 포함됩니다. 어떤 숫자는 두 번 이상 나오고 어떤 숫자는 아예 나오지 않습니다.
  2. 목록의 항목 순서가 중요합니다. 첫 번째 항목은 0, 두 번째 항목은 3 등입니다. 항목 순서는 직접 변경하지 않는 한 유지됩니다.
  3. 이전의 Codelab에서 목록에는 오름차순으로 정렬된 목록 사본을 반환하는 sorted()와 같이 다양한 내장 함수가 있음을 알아봤습니다. 프로그램에서 println() 다음에 줄을 추가하여 정렬된 목록 사본을 출력합니다.
println("sorted: ${numbers.sorted()}")
  1. 프로그램을 다시 실행하고 결과를 살펴봅니다.
list:   [0, 3, 8, 4, 0, 5, 5, 8, 9, 2]
sorted: [0, 0, 2, 3, 4, 5, 5, 8, 8, 9]

숫자를 정렬하면 목록에 각 숫자가 나타나는 횟수나 전혀 나타나지 않는지 여부를 더 쉽게 확인할 수 있습니다.

집합에 관해 알아보기

Kotlin의 또 다른 컬렉션 유형은 집합입니다. 관련 항목의 그룹이지만 목록과 달리 중복될 수 없으며 순서는 중요하지 않습니다. 집합에는 항목이 있을 수도 있고 없을 수도 있지만, 있는 항목의 경우 사본은 하나만 존재합니다. 이는 집합의 수학적 개념과 유사합니다. 내가 읽은 책의 집합을 예로 들겠습니다. 특정 책을 여러 번 읽더라도 이 책이 내가 읽은 책의 집합에 속한다는 사실은 변하지 않습니다.

  1. 다음 줄을 프로그램에 추가하여 목록을 집합으로 변환합니다.
val setOfNumbers = numbers.toSet()
println("set:    ${setOfNumbers}")
  1. 프로그램을 실행하고 결과를 살펴봅니다.
list:   [0, 3, 8, 4, 0, 5, 5, 8, 9, 2]
sorted: [0, 0, 2, 3, 4, 5, 5, 8, 8, 9]
set:    [0, 3, 8, 4, 5, 9, 2]

결과에는 원래 목록의 모든 숫자가 포함되지만, 각 숫자는 한 번씩만 나옵니다. 숫자의 순서는 원래 목록의 순서와 같지만, 집합에는 그 순서가 중요하지 않습니다.

  1. 변경 가능한 집합과 변경 불가능한 집합을 정의하고 다음 줄을 추가하여 동일한 숫자 집합이지만 다른 순서로 초기화합니다.
val set1 = setOf(1,2,3)
val set2 = mutableSetOf(3,2,1)
  1. 다음 줄을 추가하여 두 집합이 동일한지 여부를 출력합니다.
println("$set1 == $set2: ${set1 == set2}")
  1. 프로그램을 실행하고 새로운 결과를 살펴봅니다.
[1, 2, 3] == [3, 2, 1]: true

하나는 변경 가능하고 다른 하나는 그렇지 않으며 항목이 다른 순서로 정렬되어 있지만 두 집합은 정확히 동일한 항목 집합을 포함하고 있으므로 동일한 것으로 간주됩니다.

집합에서 할 수 있는 기본 작업 중 하나는 contains() 함수를 사용하여 특정 항목이 집합에 속하는지 여부를 확인하는 것입니다. 이전에 contains() 함수를 본 적이 있지만 목록에 사용했습니다.

  1. 다음 줄을 프로그램에 추가하여 집합에 7이 있는지 여부를 출력합니다.
println("contains 7: ${setOfNumbers.contains(7)}")
  1. 프로그램을 실행하고 추가 결과를 살펴봅니다.
contains 7: false

집합에 속한 값을 사용하여 테스트해볼 수도 있습니다.

위의 모든 코드는 다음과 같습니다.

fun main() {
    val numbers = listOf(0, 3, 8, 4, 0, 5, 5, 8, 9, 2)
    println("list:   ${numbers}")
    println("sorted: ${numbers.sorted()}")
    val setOfNumbers = numbers.toSet()
    println("set:    ${setOfNumbers}")
    val set1 = setOf(1,2,3)
    val set2 = mutableSetOf(3,2,1)
    println("$set1 == $set2: ${set1 == set2}")
    println("contains 7: ${setOfNumbers.contains(7)}")
}

수학의 집합과 마찬가지로 Kotlin에서도 두 집합의 교집합(∩) 또는 합집합(∪)과 같은 연산을 intersect() 또는 union()을 사용하여 실행할 수 있습니다.

맵에 관해 알아보기

이 Codelab에서 알아볼 마지막 컬렉션 유형은 또는 사전입니다. 맵은 특정 키가 부여된 값을 쉽게 찾을 수 있도록 설계된 키-값 쌍의 집합입니다. 키는 고유하며 각 키는 정확히 하나의 값에 매핑되지만, 값은 중복될 수 있습니다. 맵의 값은 문자열, 숫자, 객체일 수 있으며 목록 또는 집합과 같은 다른 컬렉션일 수도 있습니다.

b55b9042a75c56c0.png

맵은 데이터 쌍이 있는 경우에 유용하며 키를 기반으로 각 쌍을 식별할 수 있습니다. 키는 해당하는 값에 '매핑'됩니다.

  1. Kotlin 플레이그라운드에서 모든 코드를 다음 코드로 바꿉니다. 이 코드는 사람의 이름과 나이를 저장하는 변경 가능한 맵을 만듭니다.
fun main() {
    val peopleAges = mutableMapOf<String, Int>(
        "Fred" to 30,
        "Ann" to 23
    )
    println(peopleAges)
}

그러면 변경 가능한 String(키)-Int(값) 맵이 만들어지고, 두 항목이 포함된 맵이 초기화되고, 항목이 출력됩니다.

  1. 프로그램을 실행하고 결과를 살펴봅니다.
{Fred=30, Ann=23}
  1. 맵에 항목을 더 추가하려면 put() 함수를 사용하여 키와 값을 전달하면 됩니다.
peopleAges.put("Barbara", 42)
  1. 약식 표기법을 사용하여 항목을 추가할 수도 있습니다.
peopleAges["Joe"] = 51

다음은 위의 모든 코드입니다.

fun main() {
    val peopleAges = mutableMapOf<String, Int>(
        "Fred" to 30,
        "Ann" to 23
    )
    peopleAges.put("Barbara", 42)
    peopleAges["Joe"] = 51
    println(peopleAges)
}
  1. 프로그램을 실행하고 결과를 살펴봅니다.
{Fred=30, Ann=23, Barbara=42, Joe=51}

위에서 언급했듯이 키(이름)는 고유하지만 값(나이)은 중복될 수 있습니다. 동일한 키 중 하나를 사용하는 항목을 추가하려고 하면 어떻게 될까요?

  1. println() 앞에 다음 코드 줄을 추가합니다.
peopleAges["Fred"] = 31
  1. 프로그램을 실행하고 결과를 살펴봅니다.
{Fred=31, Ann=23, Barbara=42, Joe=51}

"Fred" 키는 다시 추가되지 않지만 매핑되는 값은 31로 업데이트됩니다.

여기서 확인할 수 있듯이 맵을 사용하면 키를 코드의 값에 빠르게 매핑할 수 있습니다.

3. 컬렉션 사용

컬렉션 유형마다 성질이 서로 다르지만 공통적으로 사용되는 다양한 동작이 있습니다. 변경 가능한 컬렉션의 경우 항목을 추가하거나 삭제할 수 있습니다. 모든 항목을 열거하거나, 특정 항목을 찾거나, 한 유형의 컬렉션을 다른 유형으로 변환할 수 있습니다. 이 작업은 이전에 toSet()을 사용하여 ListSet으로 변환하면서 실행해 봤습니다. 다음은 컬렉션 작업에 유용한 함수입니다.

forEach

peopleAges에 항목을 출력하고 사람의 이름과 나이를 포함한다고 가정하겠습니다. 예를 들어 "Fred is 31, Ann is 23,..."과 같은 식입니다. 이전 Codelab에서 for 루프를 배웠으므로 for (people in peopleAges) { ... }를 사용하여 루프를 작성할 수 있습니다.

하지만 컬렉션의 모든 객체를 열거하는 것은 일반적인 작업이므로, Kotlin에서는 자동으로 모든 항목을 탐색한 후 항목별로 작업을 실행하는 forEach()를 제공합니다.

  1. 플레이그라운드에서 println() 뒤에 다음 코드를 추가합니다.
peopleAges.forEach { print("${it.key} is ${it.value}, ") }

for 루프와 비슷하지만 약간 더 컴팩트합니다. forEach는 현재 항목의 변수를 지정하는 대신 특수 식별자 it을 사용합니다.

forEach() 메서드를 호출할 때 괄호를 추가할 필요가 없었습니다. 중괄호 {} 안에 코드를 전달하기만 하면 됩니다.

  1. 프로그램을 실행하고 추가 결과를 살펴봅니다.
Fred is 31, Ann is 23, Barbara is 42, Joe is 51,

원하는 것과 매우 흡사하지만 끝에 쉼표가 추가되어 있습니다.

컬렉션을 문자열로 변환하는 것은 일반적인 작업이며, 끝에 구분 기호가 추가되는 것도 일반적인 문제입니다. 이 문제를 처리하는 방법은 이후 단계에서 다룹니다.

map

map() 함수(위에서 설명한 맵 또는 사전 컬렉션과 혼동해서는 안 됨)는 컬렉션의 각 항목에 변환을 적용합니다.

  1. 프로그램에서 forEach 문을 다음 줄로 바꿉니다.
println(peopleAges.map { "${it.key} is ${it.value}" }.joinToString(", ") )
  1. 프로그램을 실행하고 추가 결과를 살펴봅니다.
Fred is 31, Ann is 23, Barbara is 42, Joe is 51

쉼표가 추가되지 않은, 올바른 출력을 얻었습니다. 한 줄에 다양한 사항이 있으므로 자세히 살펴보겠습니다.

  • peopleAges.mappeopleAges의 각 항목에 변환을 적용하고 변환된 항목으로 이루어진 새 컬렉션을 만듭니다.
  • 중괄호 {} 안에 있는 부분은 각 항목에 적용할 변환을 정의합니다. 변환은 키-값 쌍을 가져와서 문자열로 변환합니다. 예를 들어 <Fred, 31>Fred is 31로 변환합니다.
  • joinToString(", ")은 변환된 컬렉션의 각 항목을 문자열에 추가하고 ,로 구분하며 마지막 항목에는 기호를 추가하지 않습니다.
  • 이 모든 과정이 이전 Codelab에서 함수 호출 및 속성 액세스에서 실행한 것처럼 점 연산자(.)로 결합됩니다.

filter

컬렉션의 또 다른 작업은 특정 조건과 일치하는 항목을 찾는 것입니다. filter() 함수는 컬렉션에서 표현식을 기반으로 일치하는 항목을 반환합니다.

  1. println() 뒤에 다음 줄을 추가합니다.
val filteredNames = peopleAges.filter { it.key.length < 4 }
println(filteredNames)

filter 호출에는 괄호가 필요 없으며, it은 목록의 현재 항목을 나타냅니다.

  1. 프로그램을 실행하고 추가 결과를 살펴봅니다.
{Ann=23, Joe=51}

이 경우 표현식은 키(String)의 길이를 가져와서 4글자 미만인지 여부를 확인합니다. 즉, 이름이 4글자 미만인 항목이 일치하여 새 컬렉션에 추가됩니다.

맵에 필터를 적용할 때 반환되는 유형은 새 맵(LinkedHashMap)입니다. 새 맵에서 추가로 처리하거나 새 맵을 목록 같은 다른 유형의 컬렉션으로 변환할 수 있습니다.

4. 람다 및 고차 함수에 관해 알아보기

람다

이 이전 예를 다시 살펴보겠습니다.

peopleAges.forEach { print("${it.key} is ${it.value}") }

함수(forEach)가 호출되는 변수(peopleAges)가 있습니다. 함수 이름 다음에 매개변수가 포함된 괄호를 사용하는 대신 함수 이름 다음에 코드를 포함하는 중괄호 {}를 사용한 것을 볼 수 있습니다. 이전 단계에서 map 함수와 filter 함수를 사용하는 코드에도 동일한 패턴이 나타납니다. forEach 함수는 peopleAges 변수에서 호출되며 중괄호 안에 코드를 사용합니다.

중괄호 안에 작은 함수를 작성하는 것과 같지만 함수 이름은 없습니다. 이렇게 이름이 없으며 곧바로 표현식으로 사용할 수 있는 함수는 아주 유용한 개념으로, 람다 표현식 또는 간략하게 람다라고 합니다.

Kotlin을 사용하여 강력한 방식으로 함수와 상호작용하는 방법이라는 중요한 주제로 연결됩니다. 함수를 변수와 클래스에 저장하고, 함수를 인수로 전달하고, 함수를 반환할 수도 있습니다. 함수를 Int 또는 String 같은 다른 유형의 변수처럼 취급할 수도 있습니다.

함수 유형

이러한 유형의 동작을 사용할 수 있도록 Kotlin에서는 함수 유형이라는 것이 제공됩니다. 입력 매개변수와 반환 값을 기반으로 특정 유형의 함수를 정의할 수 있습니다. 다음과 같은 형식으로 표시됩니다.

함수 유형의 예: (Int) -> Int

위 유형의 함수는 Int 유형의 매개변수를 사용하고 Int 유형의 값을 반환해야 합니다. 함수 유형 표기에서 괄호 안에 매개변수를 나열합니다(매개변수가 여러 개 있는 경우 쉼표로 구분). 다음에 화살표 ->를 배치한 후 반환 유형을 나열합니다.

이 기준을 충족하는 함수 유형은 무엇일까요? 아래와 같이 정수 입력 값에 3을 곱하는 람다 표현식이 있을 수 있습니다. 람다 표현식 구문의 경우 매개변수가 먼저 나오고(빨간색 상자로 강조표시됨) 함수 화살표가 나온 다음에 함수 본문(보라색 상자로 강조표시됨)이 나옵니다. 람다의 마지막 표현식은 반환 값입니다.

252712172e539fe2.png

아래 다이어그램과 같이 람다를 변수에 저장할 수도 있습니다. 구문은 Int와 같은 기본 데이터 유형의 변수를 선언하는 방법과 유사합니다. 변수 이름(노란색 상자), 변수 유형(파란색 상자), 변수 값(녹색 상자)을 살펴봅니다. triple 변수는 함수를 저장합니다. 함수 유형이 (Int) -> Int이고 값은 람다 표현식 { a: Int -> a * 3}입니다.

  1. 플레이그라운드에서 다음 코드를 사용해봅니다. 여기에 숫자(예: 5)를 전달하여 triple 함수를 정의하고 호출합니다. 4d3f2be4f253af50.png
fun main() {
    val triple: (Int) -> Int = { a: Int -> a * 3 }
    println(triple(5))
}
  1. 결과 출력은 다음과 같습니다.
15
  1. 중괄호 안에서 매개변수의 명시적인 선언(a: Int)을 생략하고 함수 화살표(->)를 생략하고 함수 본문만 사용할 수 있습니다. main 함수에 선언된 triple 함수를 업데이트하고 코드를 실행합니다.
val triple: (Int) -> Int = { it * 3 }
  1. 출력된 내용은 이전과 같지만, 이번에는 람다가 더 간결하게 작성되었습니다. 람다의 예를 더 보려면 이 리소스를 확인하세요.
15

고차 함수

이제 Kotlin의 매우 유연한 함수 조작 방법을 확인하기 시작했으므로 또 다른 강력한 개념인 고차 함수에 관해 알아보겠습니다. 이는 함수(이 경우에는 람다)를 다른 함수로 전달하거나 다른 함수에서 함수를 반환하는 것을 의미합니다.

map, filter, forEach 함수는 모두 매개변수로 함수를 사용했으므로 고차 함수의 예입니다. 이 filter 고차 함수에 전달되는 람다에서는 단일 매개변수와 화살표 기호를 생략하고 it 매개변수도 사용할 수 있습니다.

peopleAges.filter { it.key.length < 4 }

다음은 새로운 고차 함수 sortedWith()의 예입니다.

문자열 목록을 정렬하려면 내장된 sorted() 메서드를 컬렉션에 사용하면 됩니다. 그러나 문자열 길이를 기준으로 목록을 정렬하려면 두 문자열의 길이를 가져와 비교하는 코드를 작성해야 합니다. Kotlin에서는 람다를 sortedWith() 메서드에 전달하여 작성할 수 있습니다.

  1. 플레이그라운드에서 이름 목록을 만들고 다음 코드를 사용하여 이름을 기준으로 정렬해 출력합니다.
fun main() {
    val peopleNames = listOf("Fred", "Ann", "Barbara", "Joe")
    println(peopleNames.sorted())
}
  1. 이제 람다를 sortedWith() 함수에 전달하여 이름의 길이를 기준으로 정렬된 목록을 출력합니다. 람다는 동일한 유형의 두 매개변수를 사용하고 Int를 반환해야 합니다. main() 함수의 println() 문 뒤에 다음 코드 줄을 추가합니다.
println(peopleNames.sortedWith { str1: String, str2: String -> str1.length - str2.length })
  1. 프로그램을 실행하고 결과를 살펴봅니다.
[Ann, Barbara, Fred, Joe]
[Ann, Joe, Fred, Barbara]

sortedWith()에 전달된 람다에는 두 매개변수, 즉 Stringstr1Stringstr2가 있습니다. 그런 다음 함수 화살표가 나온 후에 함수 본문이 나옵니다.

7005f5b6bc466894.png

람다의 마지막 표현식은 반환 값입니다. 이 경우에는 첫 번째 문자열의 길이와 두 번째 문자열 길이의 차이(Int)를 반환합니다. 정렬에 필요한 값과 일치시킵니다. str1str2보다 짧으면 0보다 작은 값을 반환합니다. str1str2의 길이가 같은 경우 0을 반환합니다. str1str2보다 긴 경우 0보다 큰 값을 반환합니다. sortedWith() 함수는 두 Strings 사이의 일련의 비교를 한꺼번에 처리하여 이름 길이를 기준으로 오름차순으로 목록을 출력합니다.

Android의 OnClickListener 및 OnKeyListener

지금까지 Android에서 배운 내용을 떠올려 보면 Tip Calculator 앱에서 버튼의 클릭 리스너를 설정했을 때를 포함해 이전 Codelab에서 람다를 사용한 적이 있습니다.

calculateButton.setOnClickListener{ calculateTip() }

람다를 사용하여 클릭 리스너를 설정하는 방법은 약식이며 편리합니다. 위의 코드를 긴 형식으로 작성하는 방법은 다음과 같으며 축약 버전과 비교해 볼 수 있습니다. 긴 형식의 버전에 관해 세부정보를 모두 이해할 필요는 없지만, 두 버전 간의 일부 패턴을 관찰할 수 있습니다.

29760e0a3cac26a2.png

람다에 OnClickListeneronClick() 메서드와 동일한 함수 유형이 있는 경우를 관찰합니다(View 인수 하나를 사용하고 반환 값이 없음을 의미하는 Unit을 반환함).

Kotlin의 단일 추상 메서드(SAM) 변환 때문에 축약 버전의 코드가 가능합니다. Kotlin은 람다를 단일 추상 메서드 onClick()을 구현하는 OnClickListener 객체로 변환합니다. 람다의 함수 유형이 추상 함수의 함수 유형과 일치하는지 확인하면 됩니다.

view 매개변수는 람다에서 사용되지 않으므로 이 매개변수를 생략해도 됩니다. 그런 다음 람다에 함수 본문을 배치하면 됩니다.

calculateButton.setOnClickListener { calculateTip() }

이러한 개념은 어려우며 개념에 익숙해지는 데 시간과 경험이 필요하므로 조급해 하지 않아도 됩니다. 다른 예를 살펴보겠습니다. 팁 계산기의 'Cost of Service' 텍스트 필드에 키 리스너를 설정했으므로 Enter 키를 누르면 터치 키보드가 숨겨질 수 있습니다.

costOfServiceEditText.setOnKeyListener { view, keyCode, event -> handleKeyEvent(view, keyCode) }

OnKeyListener를 살펴보면 추상 메서드에 onKey(View v, int keyCode, KeyEvent event) 매개변수가 있으며 Boolean이 반환됩니다. Kotlin의 SAM 변환 때문에 람다를 setOnKeyListener()에 전달할 수 있습니다. 람다에 함수 유형 (View, Int, KeyEvent) -> Boolean이 있으면 됩니다.

다음은 위에서 사용된 람다 표현식의 다이어그램입니다. 매개변수는 view, keyCode, event입니다. 함수 본문은 전달된 매개변수를 사용하고 Boolean을 반환하는 handleKeyEvent(view, keyCode)로 구성됩니다.

f73fe767b8950123.png

5. 단어 목록 만들기

이제 컬렉션, 람다, 고차 함수에 관해 학습한 모든 내용을 실제 사용 사례에 적용해보겠습니다.

단어 게임을 하거나 어휘를 학습하기 위한 Android 앱을 만든다고 가정하겠습니다. 앱은 다음과 같이 알파벳 글자별 버튼이 있는 모양일 수 있습니다.

7539df92789fad47.png

A를 클릭하면 A자로 시작하는 단어 목록이 간략하게 표시됩니다.

단어 컬렉션이 필요합니다. 하지만 어떤 종류의 컬렉션이어야 할까요? 앱에 알파벳의 각 글자로 시작하는 단어를 포함하려는 경우에는 특정 글자로 시작하는 모든 단어를 찾거나 정리하는 방법이 필요합니다. 더 어렵게 만들려면 사용자가 앱을 실행할 때마다 컬렉션에서 다른 단어를 선택하는 것이 좋습니다.

먼저 단어 목록부터 시작합니다. 실제 앱에서는 더 긴 단어 목록을 만들고 알파벳의 모든 글자로 시작하는 단어를 포함하겠지만 지금은 짧은 목록으로 충분히 작업할 수 있습니다.

  1. Kotlin 플레이그라운드에서 코드를 다음 코드로 바꿉니다.
fun main() {
    val words = listOf("about", "acute", "awesome", "balloon", "best", "brief", "class", "coffee", "creative")
}
  1. B 자로 시작하는 단어의 컬렉션을 가져오려면 람다 표현식과 함께 filter를 사용하면 됩니다. 다음 줄을 추가합니다.
val filteredWords = words.filter { it.startsWith("b", ignoreCase = true) }
println(filteredWords)

지정된 문자열로 시작하는 문자열의 경우 startsWith() 함수가 true를 반환합니다. 대소문자를 구분하지 않도록 지시하여 'b'가 'b'나 'B'와 일치하도록 할 수도 있습니다.

  1. 프로그램을 실행하고 결과를 살펴봅니다.
[balloon, best, brief]
  1. 앱에서 단어 순서를 무작위로 지정하고 싶습니다. Kotlin 컬렉션을 사용하면 shuffled() 함수를 사용하여 항목이 무작위로 섞인 컬렉션 사본을 만들 수 있습니다. 필터링된 단어도 섞이도록 변경합니다.
val filteredWords = words.filter { it.startsWith("b", ignoreCase = true) }
    .shuffled()
  1. 프로그램을 실행하고 새로운 결과를 살펴봅니다.
[brief, balloon, best]

단어가 무작위로 섞이므로 다른 순서로 정렬된 것을 확인할 수도 있습니다.

  1. (특히 실제 단어 목록이 긴 경우) 모든 단어가 아닌 몇 개의 단어만 필요합니다. take() 함수를 사용하여 컬렉션의 첫 번째 항목을 가져올 수 있습니다. 필터링된 단어에 처음 2개의 뒤섞인 단어가 포함되도록 합니다.
val filteredWords = words.filter { it.startsWith("b", ignoreCase = true) }
    .shuffled()
    .take(2)
  1. 프로그램을 실행하고 새로운 결과를 살펴봅니다.
[brief, balloon]

무작위로 섞기 때문에 실행할 때마다 다른 단어가 표시될 수 있습니다.

  1. 마지막으로, 앱에서 정렬된 글자별로 무작위 단어 목록을 구합니다. 이전과 마찬가지로 sorted() 함수를 사용하여 정렬된 항목이 포함된 컬렉션 사본을 반환할 수 있습니다.
val filteredWords = words.filter { it.startsWith("b", ignoreCase = true) }
    .shuffled()
    .take(2)
    .sorted()
  1. 프로그램을 실행하고 새로운 결과를 살펴봅니다.
[balloon, brief]

위의 전체 코드는 다음과 같습니다.

fun main() {
    val words = listOf("about", "acute", "awesome", "balloon", "best", "brief", "class", "coffee", "creative")
    val filteredWords = words.filter { it.startsWith("b", ignoreCase = true) }
        .shuffled()
        .take(2)
        .sorted()
    println(filteredWords)
}
  1. 코드를 변경하여 C 자로 시작하는 임의의 단어 한 개로 구성된 목록을 만들어 봅니다. 위의 코드에서 무엇을 변경해야 할까요?
val filteredWords = words.filter { it.startsWith("c", ignoreCase = true) }
    .shuffled()
    .take(1)

실제 앱에서는 알파벳 글자별로 필터를 적용해야 하지만 지금은 글자별 단어 목록을 생성하는 방법을 알고 있습니다.

컬렉션은 강력하고 유연합니다. 기능이 다양하고, 원하는 작업을 여러 방법으로 할 수 있습니다. 프로그래밍에 관해 자세히 배우면서 문제에 가장 적합한 컬렉션의 유형을 파악하고 가장 좋은 처리 방법을 알아내는 방법을 익히게 됩니다.

람다와 고차 함수를 사용하면 컬렉션 작업이 더 간편하고 간결해집니다. 매우 유용한 개념이므로 계속해서 사용하게 될 것입니다.

6. 요약

  • 컬렉션은 관련 항목의 그룹입니다.
  • 컬렉션은 변경 가능하거나 불가능합니다.
  • 컬렉션은 순서가 지정되거나 지정되지 않을 수 있습니다.
  • 컬렉션에 고유 항목이 필요하거나 중복 항목이 허용될 수 있습니다.
  • Kotlin은 목록, 집합, 맵을 포함한 다양한 종류의 컬렉션을 지원합니다.
  • Kotlin은 컬렉션을 처리하고 변환하기 위한 forEach, map, filter, sorted 등의 많은 함수를 제공합니다.
  • 람다는 이름이 없으며 곧바로 표현식으로 전달할 수 있는 함수입니다. 예를 들면 다음과 같습니다. { a: Int -> a * 3 }
  • 고차 함수는 함수를 다른 함수로 전달하거나 다른 함수에서 함수를 반환하는 것을 의미합니다.

7. 자세히 알아보기