본문 바로가기

Kotlin/Basic

Kotlin - 컬렉션 함수 (Collection Functions)

내가 코틀린을 배우면서 코틀린에 있어 특별하다고 생각되는 부분들 또는 메모해두어야 할 점들을 여기에 적어놓으려고 한다.

 

컬렉션 함수

컬렉션의 요소들을 순회할 때 for 문을 쓰면 편리하지만, 함수형 프로그래밍을 지향하는 코틀린은 컬렉션을 다룰 때 필요한 여러 가지 유용한 함수들을 지원한다. 컬렉션 함수는 list나 set, map, 또는 배열 (array) 에 일반 함수 또는 람다 함수 형태를 사용하여 for 문 없이도 아이템을 순회하며 참조하거나 조건을 걸고, 구조의 변경까지 가능한 여러 함수를 지칭한다.

 

forEach

아래 예시와 같이 사용하며, it이라는 키워드로 각각의 요소에 대응하여 안에 들어있는 함수를 실행한다.

 

예시

 

fun main() {
    var a: List<Int> = listOf(1, 2, 3)
    
    a.forEach {
        println(it + 1)
    }
}

 

출력

 

2
3
4

 

filter

조건에 맞는 요소만 컬렉션으로 다시 묶어 반환해주는 함수이다. 다음과 같이 사용한다.

 

예시

 

fun main() {
    var a: List<Int> = listOf(1, 2, 3)
    
    var b = a.filter {
        it > 1
    }
    
    println(b)
}

 

출력

 

[2, 3]


map

각각의 요소에 수식 또는 함수들을 적용한 컬렉션을 반환한다. 다음과 같이 사용할 수 있다.

 

예시

 

fun main() {
    var a: List<Int> = listOf(1, 2, 3)
    
    var b = a.map {
        it * 2
    }
    
    println(b)
}

 

출력

 

[2, 4, 6]

 

any, all, none

모두 boolean을 반환하는 함수로, 요소를 모두 검사하여 해당 조건에 맞으면 true/false를 반환하는 함수이다. 다음과 같이 사용한다.

 

예시

 

fun main() {
    var a: List<Int> = listOf(1, 2, 3)
    
    if (a.any{ it==2 }) {
        println("any: 하나라도 조건에 맞으면 true를 반환")
    }
    
    if (a.all{ it is Int }) {
        println("all: 모두 조건에 맞으면 true를 반환")
    }
    
    if (a.none{ it > 4 }) {
        println("none: 하나도 조건에 맞지 않으면 true를 반환")
    }
}

 

출력

 

any: 하나라도 조건에 맞으면 true를 반환
all: 모두 조건에 맞으면 true를 반환
none: 하나도 조건에 맞지 않으면 true를 반환

 

first, last, find, findLast, firstOrNull, lastOrNull

first는 두 가지 방법으로 쓸 수 있는데, 함수 형태로 사용을 하면 첫 번째 아이템을 반환하지만, 중괄호 ({, })에 넣은 채로 조건을 넣어 사용하면 조건에 맞는 첫 번째 아이템을 반환하게 된다. last도 first와 같은 기능을 하지만 반대 순서부터 진행 (마지막에서 가장 가까운) 한다는 점이 다르다.

 

예시

 

fun main() {
    var a: List<Int> = listOf(1, 2, 3, 4)
    
    println(a.first())
    println(a.first{ it > 1 })
    
    println(a.last())
    println(a.last{ it < 4 })
}

 

출력

 

1
2
4
3

 

first와 last는 각각 find와 findLast 함수로 대체될 수 있다.

 

first와 last를 사용할 때에는 주의할 점이 있는데, 만약 해당 조건에 맞는 아이템이 없을 경우, NoSuchElementException 예외가 일어난다는 점이다. 이 때에는 firstOrNull 또는 lastOrNull 함수를 사용하면 해당하는 아이템이 없을 경우에 null을 반환해준다.

count

일반 함수로 사용 시에는 해당 컬렉션의 아이템 개수를 반환한다. 그러나 중괄호를 걸고 조건을 안에 넣어주면, 조건에 맞는 아이템의 개수를 반환하게 된다.

 

예시

 

fun main() {
    var a: List<Int> = listOf(1, 2, 3, 4)
    
    println(a.count())
    println(a.count{ it > 2 })
}

 

출력

 

4
2

 

associateBy

아이템에서 key를 뽑아내어 map으로 만드는 함수이다. 컬렉션에 있는 객체들의 특정한 속성을 key로 삼은 map을 만들고 싶을 때 사용할 수 있다.

 

예시

 

fun main() {
    var people: List<Person> = listOf(Person("beom", 24),
                                      Person("seok", 12),
                                      Person("kang", 36))
    var map = people.associateBy{ it.name }
    
    println(map)
}

class Person(val name: String, var age: Int)

 

출력

 

{beom=Person@6b884d57, seok=Person@38af3868, kang=Person@77459877}

 

보면 각각의 Person 객체 (이미 존재하던 객체) 가 지정한 속성을 key로 갖는 map의 value로 들어간 것을 볼 수 있다.

 

groupBy

key를 기준으로 그룹을 만들어 해당 값을 가진 객체끼리 묶은 배열을 value로 갖는 map을 반환하는 함수이다.

 

예시

 

fun main() {
    var people: List<Person> = listOf(Person("beom", 12),
                                      Person("seok", 12),
                                      Person("kang", 36))
    var map = people.groupBy{ it.age }
    
    println(map)
}

class Person(val name: String, var age: Int)

 

출력

 

{12=[Person@2a84aee7, Person@a09ee92], 36=[Person@30f39991]}

 

age가 12인 객체끼리 묶인 list가 12라는 key의 value로 지정된 것을 볼 수 있다.

 

partition

아이템에 조건을 걸어 true인지 false인지에 따라 두 컬렉션으로 나누어 주는 함수이다. 두 컬렉션이 따로 반환되는 것이 아닌 Pair라는 클래스의 객체로 반환되므로 각각의 컬렉션을 first, second로 참조하여 사용하면 된다.

 

예시

 

fun main() {
    var people: List<Person> = listOf(Person("beom", 12),
                                      Person("seok", 24),
                                      Person("kang", 36))
    var pair = people.partition{ it.age > 20 }
    
    println("first: ${pair.first}")
    println("second: ${pair.second}")
}

class Person(val name: String, var age: Int)

 

출력

 

first: [Person@30f39991, Person@452b3a41]
second: [Person@23fc625e]

 

또는 변수 각각에 다음과 같이 넣을 수도 있다.

 

예시

 

fun main() {
    var people: List<Person> = listOf(Person("beom", 12),
                                      Person("seok", 24),
                                      Person("kang", 36))
    var (ageOver20, ageUnder20) = people.partition{ it.age > 20 }
    
    println(ageOver20)
    println(ageUnder20)
}

class Person(val name: String, var age: Int)

 

출력

 

[Person@30f39991, Person@452b3a41]
[Person@23fc625e]

 

flatMap

아이템마다 원하는 컬렉션을 만들어 만들어진 컬렉션들을 합쳐서 (flat하게) 반환하는 함수이다. 다음과 같이 사용한다.

 

예시

 

fun main() {
    var nums: List<Int> = listOf(1, 3, 5)
    
    var flat = nums.flatMap {
        listOf(it * 2, it + 6)
    }
    
    println(flat)
}

 

출력

 

[2, 7, 6, 9, 10, 11]

 

위의 예시를 보면, 첫 번째 아이템인 1에 있어 flatMap에서 만들어진 리스트는 [2, 7]일 것이다. 두 번째 아이템인 3에 있어 만들어진 리스트는 [6, 9], 그리고 5에 있어서는 [10, 11] 이다. 따라서 이 모든 리스트들을 flat하게 하나의 컬렉션으로 합쳤으므로 결과는 [2, 7, 6, 9, 10, 11]이 된다.

 

getOrElse

이는 컬렉션에서 소괄호 안의 index에 해당하는 아이템이 존재할 경우, 해당 아이템을 반환하고, 존재하지 않을 경우 중괄호 안의 값을 반환하는 함수이다.

 

예시

 

fun main() {
    var nums: List<Int> = listOf(1, 3, 5)
    
    println(nums.getOrElse(2) { 10 }) // nums[2] 가 존재하므로 5 반환
    println(nums.getOrElse(5) { 10 }) // nums[5] 가 없으므로 10 반환
}

 

출력

 

5
10

 

zip

파이썬의 zip과 비슷한 기능을 하는 함수이다. 두 개의 컬렉션 사이에 zip이라는 키워드를 넣어 사용하며, 두 컬렉션에 포함된 아이템들을 1:1로 매칭시켜 Pair 객체를 만들어 List에 넣어 반환하는 함수이다. 이 때, 두 컬렉션의 크기가 맞지 않을 경우, 더 작은 컬렉션의 개수만큼의 Pair 객체 아이템이 들어간 List를 만들어 반환하게 된다.

 

예시

 

fun main() {
    var nums: List<Int> = listOf(1, 3, 5)
    var strings: List<String> = listOf("one", "three", "five", "seven")
    
    for ((num, string) in nums zip strings) {
        println("${num}: ${string}")
    }
}

 

출력

 

1: one
3: three
5: five

 

출처

https://www.youtube.com/playlist?list=PLQdnHjXZyYadiw5aV3p6DwUdXV2bZuhlN