App노자

[Kotlin] 컬렉션의 확장 함수 본문

Android/Kotlin

[Kotlin] 컬렉션의 확장 함수

앱의노예 2023. 8. 4. 21:44

1. 컬렉션이란?


컬렉션이란 자주 사용하는 기초적인 자료구조를 모아 놓은 일종의 프레임워크이다

코틀린의 컬렉션은 자바 컬렉션의 구조를 확장 구현한 것이다

컬렉션의 종류로는 List, Set, Map 등이 있으며 불변형과 가변형으로 나뉘며

가변형 컬렉션은 객체에 데이터를 추가하거나 변경할 수 있고

불변형 컬렉션은 데이터를 한번 할당하면 읽기 전용이 된다

2. 연산자(operators) 기능 메서드


fun main() {
    val list1: List<String> = listOf("one", "two", "three")
    val list2: List<Int> = listOf(1, 3, 4)
    val map1 = mapOf("hi" to 1, "hello" to 2, "Goodbye" to 3)

    println(list1 + "four")  // + 연산자를 사용한 문자열 요소 추가
    println(list2 + 1) // + 연산자를 사용한 정수형 요소 추가
    println(list2 + listOf(5, 6, 7))  // 두 List의 병합
    println(list2 - 1) // 요소의 제거
    println(list2 - listOf(3, 4, 5)) // 일치하는 요소의 제거
    println(map1 + Pair("Bye", 4)) // Pair()를 사용한 map의 추가
    println(map1 - "hello") // 일치하는 값의 제거
    println(map1 + mapOf("Apple" to 4, "Orange" to 5)) // map의 병합
    println(map1 - listOf("hi", "hello")) // List에 일치하는 값을 map에서 제거
}

연산자를 사용하면 컬렉션에 대해 더하거나 빼는 등의 기능을 수행할 수 있다

일방적인 연산자를 사용해 컬렉션 요소를 더하거나 뺄 수 있고 컬렉션 자체를 더하거나 뺄 수 있다

이때 listOf(), Pair(), mapOf() 등을 더하거나 빼는 방법으로 요소를 병합하거나 제거할 수 있다

3. 집계(Aggregator) 기능 메서드


fun main() {
    val list = listOf(1, 2, 3, 4, 5, 6)
    val listPair = listOf(Pair("A", 300), Pair("B", 200), Pair("C", 100))
    val listMixed = listOf(1, "Hello", 3, "World", 5, 'A')
    val listWithNull = listOf(1, null, 3, null, 5, 6)
    val listRepeated = listOf(2, 2, 3, 4, 5, 5, 6)
    val map = mapOf(11 to "Java", 22 to "Kotlin", 33 to "C++")

    // forEach: 각 요소를 람다식으로 처리 후 컬렉션을 반환하지 않는다
    list.forEach { print("$it ") }
    println()
    list.forEachIndexed { index, value -> println("index[$index]: $value") }

    // onEach: 각 요소를 람다식으로 처리 후 컬렉션으로 반환
    val returnedList = list.onEach { print(it) }
    println()
    val returnedMap = map.onEach { println("key: ${it.key}, value: ${it.value}") }
    println("returnedList = $returnedList")
    println("returnedMap = $returnedMap")

    // count: 조건에 맞는 요소 갯수 반환
    println(list.count { it % 2 == 0 }) // 3

    // max/min: 가장 높은/낮은 요소의 반환
    println(list.max()) // 6
    println(list.min()) // 1

    // maxBy/minBy: 최대/최솟값으로 나온 요소 it에 대한 식의 결과
    println("maxBy: " + map.maxBy { it.key }) // 키를 기준으로 최댓값
    println("minBy: " + map.minBy { it.key }) // 키를 기준으로 최솟값

    // fold: 초기값과 정해진 식에 따라 처음요소부터 끝 요소에 적용하며 값을 생성
    println(list.fold(4) { total, next -> total + next }) // 4+ 1 + ... 6 = 25
    println(list.fold(1) { total, next -> total * next }) // 1 * 1 * 2 *... 6 = 720
    
    // foldRight: fold와 같고 마지막 요소에서 처음요소로 반대로 적용
    println(list.foldRight(4) { total, next -> total + next })
    println(list.foldRight(1) { total, next -> total * next })

    // reduce: fold와 동일하지만 초기값을 사용하지 않음
    println(list.reduce { total, next -> total + next })
    println(list.reduceRight { total, next -> total + next })

    // sumBy: 식에 의해 도출된 모든 요소를 합함
    println(listPair.sumBy { it.second })
}

컬렉션의 요소를 집계하는 확장 함수들이다

forEach(): 각 요소를 람다식으로 처리한 후 컬렉션을 반환하지 않는다

onEach(): 각 요소를 람다식으로 처리한 후 컬렉션을 반환한다

count(): 특정 조건에 일치하는 요소의 개수를 반환한다

max/min(): 가장 높은/낮은 요소의 반환한다

maxBy/minBy(): 람다식에 의해 컬렉션의 요소를 처리하며 키를 기준으로 최댓값과 최솟값인 요소를 반환한

fold(): 초깃값과 정해진 식에 따라 처음 요소부터 끝 요소에 적용해 값을 반환한다

reduce: 정해진 식에 따라 처음 요소부터 끝 요소에 적용해 값을 반환한다

sumBy: 식에서 도출된 모든 요소를 합한 결과를 반환하려면 sumBy를 사용한다

4. 검사(Check) 기능 메서드


fun main() {
    val list = listOf(1, 2, 3, 4, 5, 6)
    val listPair = listOf(Pair("A", 300), Pair("B", 200), Pair("C", 100))
    val map = mapOf(11 to "Java", 22 to "Kotlin", 33 to "C++")

    // all: 모든 요소가 매치 되어야 true를 반환
    println(list.all { it < 10 })  // true
    println(list.all { it % 2 == 0 }) // false

    // any: 최소한 하나 혹은 그 이상의 특정 요소가 매치되면 true를 반환
    println(list.any { it % 2 == 0 }) // true
    println(list.any { it > 10 }) // false

    // contains: 요소가 포함되어 있으면 true
    println("contains: " + list.contains(2))  // true
    println(2 in list)  // true
    println(map.contains(11)) // true
    println(11 in map) // true

    // containsAll: 모든 요소가 포함되어 있으면 true
    println("containsAll: " + list.containsAll(listOf(1, 2, 3)))

    // none: 요소가 없으면 true, 있으면 false
    println("none: " + list.none()) // false
    println("none: " + list.none { it > 6}) // true - 6 이상은 없으므로

    // isEmpty/isNotEmpty: 컬렉션이 비어있는지 검사
    println(list.isEmpty()) // false
    println(list.isNotEmpty()) // true

}

검사 기능의 메서드는 요소를 검사하고 순환하는 기능을 갖고 있다

all: 람다식에서 모든 요소가 일치할 때 true를 반환

any: any는 하나 이상의 특정 요소가 일치하면 true를 반환

contains(): 컬렉션에 특정 요소가 포함되어 있는지를 검사하기 한다 요소가 포함되어 있으면 true를 반환

containsAll(): 모든 요소가 포함되어 있는지 검사한다

none(): 요소가 없으면 true를 있으면 false를 반환한다

isEmpty(): 컬렉션이 비어 있는지 아닌지에 따라 true를 반환

isNotEmpty(): 컬렉션이 비어 있는지 아닌지에 따라 true를 반환

5. 필터(Filtering) 기능 메서드


fun main() {

    val list = listOf(1, 2, 3, 4, 5, 6)
    val listMixed = listOf(1, "Hello", 3, "World", 5, 'A')
    val listWithNull = listOf(1, null, 3, null, 5, 6)
    val listRepeated = listOf(2, 2, 3, 4, 5, 5, 6)
    val map = mapOf(11 to "Java", 22 to "Kotlin", 33 to "C++")

    // filter: 식에 따라 요소를 골라내기
    println(list.filter { it % 2 == 0 }) // [2, 4, 6] - 짝수만 골라내기
    println(list.filterNot { it % 2 == 0 }) // [1, 3, 5] - 식 이외에 요소
    println(listWithNull.filterNotNull()) // [1, 3, 5, 6] - null을 제외

    // filterIndexed: 인덱스와 함께 추출
    println("filterIndexed: " + list.filterIndexed { idx, value -> idx != 1 && value % 2 == 0 })

    // filterIndexedTo: 추출 후 mutable 컬렉션으로 변환
    val mutList =
            list.filterIndexedTo(mutableListOf()) { idx, value -> idx != 1 && value % 2 == 0 }
    println("filterIndexedTo: $mutList")

    // filterKeys/filterValues: Map의 키/값에 따른 필터
    println("filterKeys: " + map.filterKeys { it != 11 }) // {22=Kotlin, 33=C++}
    println("filterValues: " + map.filterValues { it == "Java" }) // {11=Java}

    // filterIsInstance: 여러 타입의 요소중 원하는 타입을 골라냄
    println("filterIsInstance: " + listMixed.filterIsInstance<String>())

    // slice: 특정 인덱스의 요소들을 잘라서 반환하기
    println("slice: " + list.slice(listOf(0, 1, 2))) // [1, 2, 3]

    // take: n개의 요소를 반환
    println("take: " + list.take(2)) // [1, 2]
    println("takeLast: " + list.takeLast(2)) // [5, 6] - 마지막 요소부터
    println("takeWhile: " + list.takeWhile { it < 3 }) // [1, 2] - 조건식에 따른 반환

    // drop: 처음부터 n개의 요소를 제외한 목록 List 반환
    println("drop: " + list.drop(3)) // [4, 5, 6]
    println("dropWhile: " + list.dropWhile { it < 3 }) // [3, 4, 5, 6]
    println("dropLastWhile: " + list.dropLastWhile { it > 3 }) // [1, 2, 3]

    // componentN: 각 요소를 반환
    println("component1(): " + list.component1()) // 1

    // distinct: 중복 요소는 하나로 취급해 목록 반환
    println("distinct: " + listRepeated.distinct()) // [2, 3, 4, 5, 6]

    // intersect: 교집합 요소만 골라낸다
    println("intersect: " + list.intersect(listOf(5, 6, 7, 8)))
}

필터 기능 메서드는 원하는 요소를 골라내는 기능을 수행한다

filter(): 주어진 컬렉션을 it으로 받아 식에 맞는 요소를 추출할 수 있다

filterNot(): 주어진 컬렉션을 it으로 받아 식 이외에 요소를 추출할 수 있다

filterNotNull(): null을 제외시킬때 사용한다

filterIndexed(): 2개의 인자를 람다식에서 받아서 각각 인덱스와 값에 대해 특정 수식에 맞는 조건을 추출할 수 있다

filterIndexedTo(): 2개의 인자를 람다식에서 받아서 각각 인덱스와 값에 대해 특정 수식에 맞는 조건을 추출해

가변형 컬렉션으로 변환한다

filterKeys(): 

filterValues(): Map의 키/값에 따른 필터

slice():

take():
drop():
componentN():

distinct():

intersect():

 

 

 

 

6. 요소의 매핑


fun main() {
    val list = listOf(1, 2, 3, 4, 5, 6)
    val listWithNull = listOf(1, null, 3, null, 5, 6)

    // map: 컬렉션에 주어진 식을 적용해 새로운 컬렉션을 반환
    println(list.map { it * 2 })

    // mapIndexed:  컬렉션에 인덱스를 포함해 주어진 식을 적용해 새로운 컬렉션 반환
    val mapIndexed = list.mapIndexed { index, it -> index * it }
    println(mapIndexed)

    // mapNotNull: null을 제외하고 식을 적용해 새로운 컬렉션 반환
    println(listWithNull.mapNotNull { it?.times(2) })

    // flatMap: 각요소에 식을 적용 후 다시 합쳐 새로운 컬렉션을 반환
    println(list.flatMap { listOf(it, 'A') })
    val result = listOf("abc", "12").flatMap { it.toList() }
    println(result) // [a, b, c, 1, 2]

    // groupBy: 주어진 함수의 결과에 따라 그룹화 하여 map으로 반환
    val grpMap = list.groupBy { if (it % 2 == 0) "even" else "odd" }
    println(grpMap) // {odd=[1, 3, 5], even=[2, 4, 6]}

}

매핑에 사용하는 .map()은 주어진 컬렉션의 요소에 일괄적으로 .map()에 있는 식을 적용해

새로운 컬렉션을 만들 수 있게 해주는 메서드이다 forEach()와 비슷해 보이나 주어진 컬렉션을 전혀 건드리지 않는다는 차이점을 갖고 있다

mapIndexed를 이용하면 컬렉션에 인덱스를 포함해 식을 적용하고

mapNotNull을 사용하면 null을 제외하고 컬렉션을 반환시킬 수 있다

7. 요소 처리와 검색


fun main() {
    val list = listOf(1, 2, 3, 4, 5, 6)
    val listPair = listOf(Pair("A", 300), Pair("B", 200), Pair("C", 100), Pair("D", 200))
    val listRepeated = listOf(2, 2, 3, 4, 5, 5, 6)

    // elementAt: 인덱스에 해당하는 요소 반환
    // 범위를 벗어나는 경우 IndexOutOfBoundsException 가 발생
    println("elementAt: " + list.elementAt(1))

    // elementAtOrElse: 인덱스를 벗어나는 경우 식에 따라 결과 반환 아니면 요소 반환
    println("elementAtOrElse: " + list.elementAtOrElse(10, { 2 * it }))
    // elementAtOrElse(10) { 2 * it }) 표현식과 동일

    // elementAtOrNull: 인덱스를 벗어나는 경우 null 반환
    println("elementAtOrNull: " + list.elementAtOrNull(10))

    // first: 식에 일치하는 첫 요소 반환
    println("first: " + listPair.first { it.second == 200 })

    // last: 식에 일치하는 마지막 요소 반환
    println("last: " + listPair.last { it.second == 200 })

    // firstOrNull: 식에 일치하지 않은 경우 null 반환
    println("firstOrNull: " + listPair.firstOrNull { it.first == "E" })

    // lastOrNull: 식에 일치하지 않은 경우 null 반환
    println("lastOrNull: " + listPair.lastOrNull { it.first == "E" })

    // indexOf: 주어진 요소의 일치하는 첫 인덱스 반환
    println("indexOf:" + list.indexOf(4)) // 3

    // indexOfFirst: 람다식에 일치하는 첫 요소의 인덱스 반환, 없으면 -1
    println("indexOfFirst:" + list.indexOfFirst { it % 2 == 0 }) // 1

    // lastIndexOf: 주어진 요소 일치하는 가장 마지막 인덱스 반환
    println("lastIndexOf:" + listRepeated.lastIndexOf(5)) // 5

    // indexOfLast: 람다식에 일치하는 마지막 요소의 인덱스 반환, 없으면 -1
    println("indexOfLast:" + list.indexOfLast { it % 2 == 0 }) // 5

    // single: 람다식에 일치하는 요소 하나 반환
    println("single: " + listPair.single { it.second == 100 })
    println("singleOrNull: " + listPair.singleOrNull { it.second == 500 })

    // binarySearch: 요소에 대해 이진 검색후 인덱스 반환
    println("binarySearch: " + list.binarySearch(3))

    // find: 조건식을 만족하는 첫번째 검색된 요소 반환, 없으면 null
    println("find: " + list.find { it > 3 })

}

매핑에 사용하는 .map()은 주어진 컬렉션의 요소에 일괄적으로 .map()에 있는 식을 적용해

새로운 컬렉션을 만들 수 있게 해주는 메서드이다

8. 컬렉션 분리와 병합


fun main() {
    val list1 = listOf(1, 2, 3, 4, 5, 6)
    val list2 = listOf(2, 2, 3, 4, 5, 5, 6, 7)

    // union: 두 List를 합친다 (중복요소는 하나만)
    println(list1.union(list2))

    // union: 두 List를 합친다 (중복요소 포함), + 연산자와 같음
    println(list1.plus(list2))

    // partition: 주어진 식에 따라 두개의 컬렉션으로 분리해 Pair로 반환
    // 첫번째 컬렉션은 true, 두번째 컬렉션은 식이 false일 때
    val part =  list1.partition { it % 2 == 0 }
    println(part)

    // zip: 동일 인덱스끼리 Pair를 만들어 반환
    // (반환 시 가장 작은 요소를 가진 컬렉션의 갯수 만큼만 반환)
    val zip = list1.zip(listOf(7, 8))
    println(zip)
}

union(): 두 List 컬렉션을 병합하고 중복된 요소 값은 하나만 유지한다 (Set컬렉션 반환)

plus(): 두 List 컬렉션을 중복 요소를 포함해 합친다 (List 컬렉션 반환)

partition(): 주어진 조건식의 결과에 따라 List컬렉션을 2개로 분리한(Pair 반환)

zip(): 2개의 컬렉션에서 동일한 인덱스끼리 짝을 만들어 반환한다 (Pair 반환)

'Android > Kotlin' 카테고리의 다른 글

[Kotlin] by 키워드  (0) 2023.08.06
[Kotlin] 지연 초기화  (0) 2023.08.05
[Kotlin] sequence (시퀀스)  (0) 2023.08.03
[Kotlin] Set과 Map  (0) 2023.07.30
[Kotlin] List  (0) 2023.07.22