본문 바로가기
Kotlin

[Kotlin] 컬렉션(Collection) 함수

by 태크민 2024. 11. 4.

컬렉션(Collection) 함수

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

 

forEach

아래 예시와 같이 사용하며, it이라는 키워드로 각각의 요소에 대응하여 안에 들어있는 함수를 실행한다. forEachIndexed() 라는 고차함수 역시 제공하는데, 이는 각 요소들의 값 뿐만 아니라 인덱스도 함께 사용할 수 있도록 해주는 녀석이다.

 

예시

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를 반환

 

sort()

대부분 언어가 갖고있는 내장 함수이다. 말 그대로 Collection 을 정렬해주는 역할을 수행한다. sorted() 를 활용하면 정렬이 된 새로운 객체를 반환한다. 그리고 내림차순 정렬의 경우 sortByDescending() 등을 사용할 수 있다.

함수형 프로그래밍 언어답게 sortBy() 라는 고차함수를 통해 Collection 을 구성하는 각 객체들의 특정 프로퍼티를 기준으로 정렬을 할 수 있도록 해준다. 아래 예제에서는 Pair 객체의 second 값을 기준으로 정렬하게 된다. 따라서 위 예제를 실행해보았을 때, 아래와 같은 결과를 보인다.

 

예시

fun main() {
    val a = mutableListOf(3, 2, 1)
    a.sort()  // 오름차순 정렬
    println(a)

    val sorted = a.sortedByDescending { it } // 내림차순, 정렬된 새로운 Collection 객체 반환
    println(sorted)

    // sortBy() : 각 객체가 갖고있는 프로퍼티를 기준으로 정렬
    val list = mutableListOf(1 to "A", 2 to "B", 100 to "C", 50 to "D", 10 to "E")
    list.sortBy { it.second }
    println(list)
}

 

출력

[1, 2, 3]
[3, 2, 1]
[(1, A), (2, B), (100, C), (50, D), (10, E)]

 

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

 

reduce(), fold()

Collection 을 구성하는 모든 원소들에 대해 누적합을 계산하는 함수들이다. 고차함수이기 때문에 누적합을 어떻게 쌓아올리는 지에 대해 표현식을 걸어줄 수 있다. fold() 의 경우 초기값을 설정해줄 수 있다. reduce() 는 첫 번째 요소를 acc 로 사용하고, 두 번째 요소 부터 연산하게 된다.

 

예시

fun main() {
    val a: List<Int> = listOf(1, 3, 5)

    println("Fold : ${a.fold(0) { acc, i ->
        acc + i * 2
    }}")

    println("Reduce : ${a.reduce { acc, i -> 
        acc + i * 2
    }}")
}

 

출력

Fold : 18
Reduce : 17

참고자료

Kotlin - 컬렉션 함수 (Collection Functions)

 

Kotlin - 컬렉션 함수 (Collection Functions)

내가 코틀린을 배우면서 코틀린에 있어 특별하다고 생각되는 부분들 또는 메모해두어야 할 점들을 여기에 적어놓으려고 한다. 컬렉션 함수 컬렉션의 요소들을 순회할 때 for 문을 쓰면 편리하지

lgphone.tistory.com

[Kotlin] Collection 날먹하는 함수들

 

[Kotlin] Collection 날먹하는 함수들

이런 것도 안 되는 흑우 언어 없제?

velog.io

[Kotlin] 컬렉션(Collection)의 확장함수

 

[Kotlin] 컬렉션(Collection)의 확장함수

모든 내용은 Do it! 안드로이드 앱 프로그래밍을 바탕으로 정리한 것입니다. [이전 포스팅] 코틀린의 컬렉션 - List, Set, Map에 대한 내용은 아래 포스팅 참고 [Kotlin] 컬렉션(Collection) - List 모든 내용

junyoung-developer.tistory.com

 

'Kotlin' 카테고리의 다른 글

[Kotlin] 확장(Extension) 함수  (5) 2024.11.06
[Kotlin] 코틀린에서의 Null 처리 방법  (4) 2024.11.05
[Kotlin] by란?  (0) 2024.11.02
[Kotlin] open class와 abstract class  (0) 2024.11.02
[Kotlin] 깊은 복사 (Deep Copy) 3가지 방법  (0) 2024.11.02