[Kotlin] Ch5-3. 컬렉션의 확장 함수

leeeha·2022년 9월 6일
0

코틀린

목록 보기
26/29
post-thumbnail

출처: https://www.boostcourse.org/mo234/lecture/154318?isDesc=false

코틀린은 컬렉션을 위한 많은 확장 함수를 제공하고 있다.

  • 연산자 (operator): 컬렉션끼리 더하고 빼는 기능
  • 집계 (aggregator): 최대, 최소, 집합, 총합 등의 계산
  • 검사 (check): 요소 검사 및 순회
  • 필터링 (filtering): 원하는 요소 골라내기
  • 변환 (transformer): 뒤집기, 정렬, 자르기 등

컬렉션에 대한 연산

package chap05.section3

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))

    // 요소 삭제 (원본은 그대로)
    println(list2 - 1)
    println(list2 - listOf(3, 4))

    // 맵의 경우  
    println(map1 + Pair("bye", 4))
    println(map1 - "hello")
    println(map1 + mapOf("Apple" to 4, "Orange" to 5))
    println(map1 - listOf("hi", "hello"))
}

[one, two, three, four]
[1, 3, 4, 1]
[1, 3, 4, 5, 6, 7]
[3, 4]
[1]
{hi=1, hello=2, goodbye=3, bye=4}
{hi=1, goodbye=3}
{hi=1, hello=2, goodbye=3, Apple=4, Orange=5}
{goodbye=3}


요소의 처리와 집계

  • 요소의 순환: forEach, onEach (forEach는 각 요소를 람다식으로 처리한 후 컬렉션을 반환하지 않지만, onEach는 반환한다.)
  • 요소의 집계: forEach, forEachIndexed, onEach, count, max, min, maxBy, minBy, fold, reduce, sumBy 등

요소의 순환

package chap05.section3

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++")

    list.forEach { print("$it") }
    println()
    list.forEachIndexed { index, value -> println("list[$index] : $value") }

    val returnedList = list.onEach { print(it) }
    println()
    val returnedMap = map.onEach { println("key: ${it.key}, value: ${it.value}") }
    println(returnedList)
    println(returnedMap)
}

123456
list[0] : 1
list[1] : 2
list[2] : 3
list[3] : 4
list[4] : 5
list[5] : 6
123456
key: 11, value: Java
key: 22, value: Kotlin
key: 33, value: C++
[1, 2, 3, 4, 5, 6]
{11=Java, 22=Kotlin, 33=C++}

요소의 집계

  • fold, reduce: 초기값과 정해진 식에 따라 요소를 처리하기 위한 것

    • fold: 초기값과 정해진 식에 따라 처음부터 마지막 요소까지 해당 식을 적용하여 값을 반환
    • reduce: fold와 동일하지만 초기값을 사용하지 않는다.
  • foldRight, reduceRight: 위의 개념과 동일하지만 요소의 마지막 (오른쪽) 요소에서 처음 요소 순으로 적용하다.

package chap05.section3

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

    // 초기값에 따라 누적합 구하기
    println(list.fold(4) { total, next -> total + next }) // 4 + (1 + 2 + ... + 6) = 25
    println(list.fold(1) { total, next -> total * next }) // 1 * (1 * 2 * ... * 6) = 720

    // 역방향 (뒤에서 앞으로) 
    println(list.foldRight(4) { total, next -> total + next })
    println(list.foldRight(1) { total, next -> total * next })

    // 초기값 없이 첫번째 요소부터 시작
    println(list.reduce { total, next -> total + next }) // 1 + 2 + ... + 6 = 21
    println(list.reduceRight { total, next -> total + next }) // 6 + 5 + ... + 1 = 21
}

25
720
25
720
21
21


매핑 관련 연산

map()

  • 주어진 컬렉션의 모든 요소에 일괄적으로 식을 적용하여 새로운 컬렉션을 만든다.
  • forEach와 다르게 새로운 컬렉션을 반환한다. (forEach는 Unit을 리턴)
package chap05.section3

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

    // 컬렉션에 주어진 식을 적용하여 새로운 컬렉션 반환
    println(list.map { it * 2 }) // [2, 4, 6, 8, 10, 12]

    // 컬렉션에 인덱스를 포함하여 주어진 식을 적용해 새로운 컬렉션 반환
    val mapIndexed = list.mapIndexed { index, it -> index * it }
    println(mapIndexed) // [0, 2, 6, 12, 20, 30] 

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

flatMap()

각 요소에 식을 적용한 후 이것을 다시 하나로 합쳐서 새로운 컬렉션을 반환

package chap05.section3

fun main() {
	val list = listOf(1, 2, 3, 4, 5, 6)
    
    // 각 요소에 식을 적용한 뒤에 이를 다시 하나로 합쳐서 새로운 컬렉션 반환
    println(list.flatMap { listOf(it, 'A') })

	// toList(): 요소를 분해하여 리스트로 반환 
    val result = listOf("abc", "12").flatMap { it.toList() } 
    println(result) 
}

[1, A, 2, A, 3, A, 4, A, 5, A, 6, A]
[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]}


요소 처리와 검색 연산

  • 보통 인덱스와 함께 해당 요소의 값을 반환
  • 식에 따라 처리하거나 인덱스를 벗어나면 null을 반환
package chap05.section3

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

    // 인덱스에 해당하는 요소 반환 
    println("elementAt: " + list.elementAt(1))

    // 인덱스를 벗어나는 경우, 주어진 람다식에 따른 결과 반환
    println("elementAtOrElse: " + list.elementAtOrElse(10) { 2 * it })

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

elementAt: 2
elementAtOrElse: 20
elementAtOrNull: null


순서와 정렬 연산

  • reversed()
  • sorted(), sortedDescending()
  • sortedBy { it % 3 }, sortedByDescending { it % 3 }

시퀀스 (sequence)

  • 순차적으로 요소의 크기를 특정하지 않고 추후에 결정하는 특수한 컬렉션
  • 예를 들어, 특정 파일에서 라인 단위로 읽어서 요소를 만들 때
  • 시퀀스는 처리 중에는 계산하지 않고 있다가, toList()나 count() 같은 최종 연산에 의해 결정됨.

generateSequence

시드 값에 의해 시작 요소의 값이 결정됨.

package chap05.section3

fun main() {
    // 시드 값 1부터 시작해서 1씩 증가하는 시퀀스 생성
    val nums: Sequence<Int> = generateSequence(1) { it + 1 }

    // take()를 사용해서 원하는 개수만큼 요소를 획득하고
    // toList()를 통해 리스트 컬렉션으로 반환
    println(nums.take(10).toList())

    // 10개 요소의 제곱수 구하기
    val squares = generateSequence(1) { it + 1 }.map { it * it }
    println(squares.take(10).toList())

    // 홀수 요소 5개의 제곱수 구하기
    val oddSquares = squares.filter { it % 2 != 0 }
    println(oddSquares.take(5).toList())
}

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
[1, 9, 25, 49, 81]

cf) 메서드 체이닝을 연속해서 사용하면, 하나의 구문이 끝날 때마다 중간 결과로 새로운 리스트를 계속해서 만들어낸다.

asSequence()

  • 중간 연산 결과 없이 한번에 끝까지 연산한 뒤에 결과를 반환
  • filter나 map을 메서드 체이닝으로 사용하면 순차적 연산이기 때문에 시간이 많이 걸릴 수 있지만, asSequence()는 병렬 처리가 되므로 처리 성능이 더 좋아짐.
package chap05.section3

fun main() {
    val list1 = listOf(1, 2, 3, 4, 5)
    val listDefault = list1
        .map { print("map($it) "); it * it }
        .filter { println("filter($it) "); it % 2 == 0 }
    println(listDefault)

    val listSeq = list1.asSequence()
        .map { print("map($it) "); it * it }
        .filter { println("filter($it) "); it % 2 == 0}
        .toList()
    println(listSeq)
}

map(1) map(2) map(3) map(4) map(5) filter(1)
filter(4)
filter(9)
filter(16)
filter(25)
[4, 16]
map(1) filter(1)
map(2) filter(4)
map(3) filter(9)
map(4) filter(16)
map(5) filter(25)
[4, 16]

  • asSequence()를 사용하면, map과 filter와 같은 모든 연산이 수행되고 나서 최종 결과물이 새로운 리스트인 listSeq에 지정됨.
  • 요소의 개수가 많을 때는 속도나 메모리 측면에서 훨씬 좋은 성능을 낼 수 있음.
profile
습관이 될 때까지 📝

0개의 댓글