본문 바로가기
Kotlin

[Kotlin in Action] 5.2 컬렉션 함수형 API

by Nhahan 2025. 3. 18.

5.2.1 필수적인 함수: filter와 map

filter 함수는 컬렉션을 이터레이션하면서 주어진 람다에 각 원소를 넘겨서 람다가 true를 반환하는 원소만 모은다.

>>> val list = listOf(1, 2, 3, 4)
>>> pritln(list.filter { it % 2 == 0 }) // 짝수만 남는다.
[2, 4]

 

map함수는 주어진 람다를 컬렉션의 각 원소에 적용한 결과를 모아서 새 컬렉션을 만든다.

>>> val list = listOf(1, 2, 3, 4)
>>> println(list.map { it * it }
[1, 4, 9, 16]

 

5.2.2 all, any, count, find: 컬렉션에 술어 사용

어떤 사람의 나이가 27살 이하인지 판단하는 술어 함수 canBeInClub27이 있다.

val canBeInClub = { p: Person -> p.age <= 27 }

 

모든 원소가 이 술어를 만족하는지 궁금하다면 all 함수를 쓴다.

>>> val people = listOf(Person("Alice", 27), Person("Bob", 31))
>>> println(people.all(canBeInClub27))
false

 

술어를 만족하는 원소가 하나라도 있는지 궁금하면 any를 쓴다.

>>> println(people.any(canBeInClub27))
true

 

술어를 만족하는 원소의 개수를 구하려면 count를 사용한다.

>>> val people = listOf(Person("Alice", 27), Person("Bob", 31))
>>> println(people.count(canBeInClub))
1

 

함수를 적재적소에 사용하라: count와 size
count가 있다는 사실을 잊어버리고, 컬렉션을 필터링한 결과의 크기를 가져오는 경우가 있다.

>>> println(people.filter(canBeInClub27).size)

하지만 이렇게 처리하면 조건을 만족하는 모든 원소가 들어가는 중간 컬렉션이 생긴다. 반면 count는 조건을 만족하는 원소의 개수만을 추적하지 조건을 만족하는 원소를 따로 저장하지 않는다. 따라서 count가 훨씬 더 효율적이다.

 

술어를 만족하는 원소를 하나 찾고 싶으면 find 함수를 사용한다.

>>> val people = listOf(Person("Alice", 27), Person("Bob", 31))
>>> println(people.find(canBeInClub27))
Person(name=Alice, age=27)

 

5.2.3 groupBy: 리스트를 여러 그룹으로 이뤄진 맵으로 변경

특성을 파라미터로 전달하면 컬렉션을 자동으로 구분해주는 함수가 groupBy이다.

>>> val people = listOf(Person("Alice", 31), Person("Bob", 29), Person("Carol", 31))
>>> println(people.groupBy) { it.age })

{29=[Person(name=Bob, age=29)], 
31=[Person(name=Alice, age), Person(nameCarol, age=31)]}

 

5.2.4 flatMap과 flatten: 중첩된 컬렉션 안의 원소 처리

Book으로 표현한 책에 대한 정보를 저장하는 도서관이 있다고 가정하자.

class Book(val title: String, val authors: List<String>)

책마다 저자가 한 명 또는 여러 명 있다. 도서관에 있는 책의 저자를 모두 모은 집합을 다음과 같이 가져올 수 있다.

books.flatMap { it.authors }.toSet() // book 컬렉션에 있는 책을 쓴 모든 저자의 집합

flatMap 함수는 먼저 인자로 주어진 람다를 컬렉션의 모든 객체에 적용하고, 람다를 적용한 결과 얻어지는 여러 리스트를 한 리스트로 한데 모은다.

 

리스트의 리스트가 있는데 모든 중첩된 리스트의 원소를 한 리스트로 모아야 한다면 flatMap을 떠올릴 수 있을 것이다. 하지만 특별히 변환해야 할 내용이 없다면 리스트의 리스트를 평평하게 펼치기만 하면 된다. 그런 경우 listOfLists.flatten()처럼 flatten 함수를 사용할 수 있다.

 

 

flatMap 예제

fun main() {
    // 각 단어를 문자 리스트로 변환하고, 이를 모두 하나의 리스트로 합친다.
    val words = listOf("Hello", "World")
    val letters = words.flatMap { word -> 
        word.toList() // 각 문자열을 문자 리스트로 변환
    }
    println(letters) // 출력: [H, e, l, l, o, W, o, r, l, d]
}

flatMap()은 각 요소를 다른 형태의 컬렉션으로 변환한 후, 모든 결과 컬렉션을 평평하게 합쳐줍니다. 위 예제에서는 각 단어를 문자 리스트로 변환하고, 모든 문자들을 하나의 리스트로 합침

 

 

 

flatten 예제

fun main() {
    // List 안에 List를 갖는 중첩 리스트
    val nestedList = listOf(
        listOf(1, 2, 3),
        listOf(4, 5),
        listOf(6)
    )
    // flatten은 중첩된 리스트를 단일 리스트로 변환한다.
    val flatList = nestedList.flatten()
    println(flatList) // 출력: [1, 2, 3, 4, 5, 6]
}

flatten()은 List<List<T>>와 같이 중첩된 리스트를 한 단계 평평하게 만들어서 List<T>로 변환

 

 

댓글