컬렉션 (Collection) (2/2)

장똑대·2022년 4월 26일
0

Do it! 코틀린 프로그래밍 [셋째마당, 코틀린 표준 라이브러리의 활용] 학습

✏️3. 컬렉션의 확장 함수✏️

3-1. 컬렉션의 연산

  • 일반적 연산자인 +와 -를 사용해 컬렉션 요소나 컬렉션 자체를 더하거나 뺄 수 있음
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") 
    // list1 = [one, two, three, four]
    println(list2 + 1) 
    // list2 = [1, 3, 4, 1]
    println(list2 + listOf(5, 6, 7)) 
    // list2 = [1, 3, 4, 5, 6, 7]
    println(list2 - 1 ) 
    // list2 = [3, 4]
    println(list2 - listOf(3, 4, 5)) 
    // list2 = [1]
    println(map1 + Pair("Bye", 4)) 
    // map1 = { hi=1, hello=2, Goodbye=3, Bye=4 }
    println(map1 - "hello") 
    // map1 = { hi=1, Goodbye=3 }
    println(map1 + mapOf("Apple" to 4)) 
    // map1 = { hi=1, hello=2, Goodbye=3, Apple=4 }
    println(map1 - listOf("hi", "hello")) 
    // map1 = { Goodbye=3 }
}

3-2. 요소의 처리와 집계

📌 요소의 순환

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

// onEach (컬렉션 반환)
val returnList = list.onEach { print(it) }
val returnMap = map.onEach { println ("key: ${it.key}, value: ${it.value}") }

📌 요소의 개수 반환

  • println(list.count { it % 2 == 0})
    -> 2로 나누었을때 나머지가 0인 요소의 개수를 반환

📌 최댓값과 최솟값의 요소 반환

println(list.max()) // 최댓값 요소
println(list.min()) // 최솟값 요소

println(map.maxBy { it.key }) // 키를 기준으로 최댓값
println(map.minBy { it.key }) // 키를 기준으로 최솟값

📌 각 요소에 정해진 식 적용

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

// fold와 같고 마지막 요소에서 처음 요소 순서로 적용
println(list.foldRight(4) { total, next -> total + next })
println(list.foldRight(1) { total, next -> total * next })

// fold와 같고 초깃값이 없음
println(list.reduce { total, next -> total + next })
// 1 + 2 + ... + 6 = 21
println(list.reduceRight { total, next -> total + next })
// 6 + 5 + ... + 1 = 21

3-3. 요소의 검사

📌 요소의 일치여부 검사하기

  • all : 모든 요소가 일치 할 때 true를 반환
  • any : 최소한 하나 혹은 그 이상의 특정 요소가 일치하면 true 반환
println(list.all { it < 10 })
println(list.any { it > 10})

📌 특정 요소의 포함 및 존재여부 검사

  • contains() : 컬렉션에 특정 요소가 포함되어있는지 검사. 요소가 포함되어 있으면 true 반환
    -> 범위 연산자 in을 사용해서 요소 포함 여부 확인 가능
  • containsAll() : 모든 요소가 포함되어 있는지 검사
  • none() : 요소가 없으면 true 있으면 false 반환
  • isEmpty() : 컬렉션이 비어있으면 true를 반환
  • isNotEmpty() : 컬렉션이 비어있지 않으면 true를 반환
val list = listOf(1, 2, 3, 4, 5, 6)

println(list.contains(2)) // true
println(2 in list) // true
println(map.contains(11)) // true
println(list.containsAll(listOf(1, 2, 3))) // true
println(list.none()) // false
println(list.isEmpty()) // false
println(list.isNotEmpty()) // true

3-4. 요소의 필터와 추출

📌 특정 요소를 골라내기

  • filterIndexed : 특정 인덱스와 함께 추출
  • filterIndexedTo : filterIndexed에 컬렉션으로 반환되는 기능 추가
  • filterKeys : 요소를 it으로 받아 키에 대한 조건에 맞는 부분을 반환
  • filterValues : 값에 의한 조건식을 만들고 그에 맞는 부분을 반환
  • filterIsInstance<T>() : 원하는 자료형을 골라내는 기능
println(list.filter { it % 2 == 0}) // 짝수만 골라내기
println(list.filterNot { it % 2 == 0}) //식 이외의 요소 골라내기
println(list.filterNotNull()) // null을 제외

// 인덱스가 1이 아니고 짝수인 요소들
println(list.filterIndexed {idx, value -> idx != 1 && value % 2 == 0})
// 추출 후 가변형 컬렉션으로 변환
val mutList = list.filterIndexedTo(mutableListOf()) {idx, value -> idx != 1 && value % 2 == 0}

println(map.filterKeys {it != 11}) // 키 11을 제외한 요소
println(map.filterValues {it == "Java"}) 값이 "Java"인 요소

// listMixed = [1, "Hello", 3, 'A', "World"]
println(listMixed.filterIsInstance<String>()) // [Hello, World]

📌 특정 범위를 잘라내거나 반환하기

  • slice() : 특정 범위의 인덱스를 가진 List를 인자로 사용해 기존 List에서 요소들을 잘라냄
  • take() : 앞에서부터 인자의 숫자만큼 반환
  • takeLast() : 뒤에서부터 인자의 숫자만큼 반환
  • takeWhile() : 조건식에 따라 반환
val list = listOf(1, 2, 3, 4, 5, 6)

println(list.slice(listOf(0, 1, 2))) // [1, 2, 3]
println(list.take(2)) // [1, 2]
println(list.takeLast(2)) // [5, 6] 
println(list.takeWhile{ it < 3}) // [1, 2]

📌 특정 요소 제외하기

  • drop() : 앞에서부터 인자의 숫자만큼 제외
println(list.drop(3)) // [4, 5, 6]
println(list.dropWhile { it < 3 }) // [3, 4, 5, 6]
println(list.dropLastWhile { it > 3 }) // [1, 2, 3]

📌 각 요소의 반환

  • componentN() : N은 인덱스 번호가 아닌 1부터 시작하는 요소의 순서 번호
  • list.component1 : 리스트의 첫 번째 요소인 1 반환
    -> list = listOf(1, 2, 3, 4, 5, 6)
    -> 1의 인덱스는 0이지만 component는 1부터 시작하므로 component1

📌 합집합과 교집합

  • distinct() : 중복 요소가 있는 경우 1개로 취급하여 컬렉션 List로 반환
  • intersect() : 겹치는 요소만 골라내 List를 반환
val listRepeated = listOf(2, 2, 3, 4, 5, 5, 6)

println(listRepeated.distinct()) // [2, 3, 4, 5, 6]
println(list.intersect(listOf(5, 6, 7,8))) // [5, 6]

📌 요소의 매핑

  • map() : 컬렉션에 주어진 식을 적용해 새로운 컬렉션 반환
  • mapIndexed : 컬렉션에 인덱스를 포함하고 주어진 식을 적용해 새로운 컬렉션 반환
  • mapNotNull : null을 제외하고 식을 적용해 새로운 컬렉션 반환
  • flatMap : 각 요소에 식을 적용한 후 이것을 다시 하나로 합쳐 새로운 컬렉션을 반환
  • groupBy : 주어진 식에 따라 요소를 그룹화하고 이것을 다시 Map으로 반환
val list = listOf(1, 2, 3, 4, 5, 6)

println(list.map { it * 2 }) // [2, 4, 5, 6, 10, 12]
val mapIndexed = list.mapIndexed { index, it => index * it }
// [0, 2, 6, 12, 20, 30]

val listWithNull = listOf(1, null, 3, null, 5, 6)
println(listWithNull.mapNotNUll { it?.times(2) }) // [2, 6, 10, 12]

println(list.flatMap { listOf(it, 'A')}) // [1,A,2,A,3,A,4,A,5,A,6,A]

val grpMap = list.groupBy{ it (it % 2 == 0) "even" else "odd"}
// even = [2, 4, 6], odd = [1, 3, 5]

3-5. 요소 처리와 검색

  • elementAt() : 인덱스에 해당하는 요소 반환
  • elementAtOrElse() : 인덱스를 벗어나는 경우 식에 따라 결과 반환
  • elementAtOrNull() : 인덱스를 벗어나는 경우 null 반환
  • indexOf() : 인자에 지정된 요소에 대한 첫 인덱스 반환
  • indexOfFirst() : 람다식에 일치하는 해당 요소 중 첫 번째 인덱스의 값을 반환. 해당하는 요소가 없으면 -1 반환
  • indexOfLast() : 람다식에 일치하는 해당 요소 중 마지막 인덱스의 값을 반환. 해당하는 요소가 없으면 -1 반환
  • binarySearch() : 인자로 주어진 요소에 대해 이진 탐색 후 요소의 인덱스를 반환
    -> 중복된 요소가 있는 경우 해당 요소가 원하는 순서에 있는 요소인지는 보장하지 않음
  • find : 조건식을 만족하는 첫 번째 검색된 요소를 반환, 없으면 null
val list = listOf(1, 2, 3, 4, 5, 6)

println(list.elementAt(1)) // 2
println(list.elementAtOrElse(10, {2 * it}) // 20
println(list.elementAtOrNull(10)) // null
println(list.indexOf(4)) // 3 (4의 인덱스 넘버)
println(list.indexOfFirst { it % 2 == 0 }) // 1 (2의 인덱스 넘버)
println(list.indexOfLast { it % 2 == 0 }) // 5 (6의 인덱스 넘버)
println(list.binarySearch(3)) // 2
println(list.find { it > 3}) // 4

val listRepeated = listOf(2, 2, 3, 4, 5, 5, 6)
println(listRepeated.lastIndexOf(5)) // 5

  • first : 식과 일치하는 첫 번째 요소 값 반환
  • last : 식과 일치하는 마지막 요소 값 반환
  • firstOrNull : 식에 일치하지 않는 경우 null 반환
  • lastOrNull : 식에 일치하지 않는 경우 null 반환
  • single : 조건식에 일치하는 요소를 하나 반환. 일치하는 요소가 하나 이상인 경우 예외 발생
  • singleOrNull : 조건식에 일치하는 요소가 없거나 일치하는 요소가 하나 이상이면 null을 반환
val listPair = listOf(Pair("A", 200), Pair("B", 100), Pair("C", 200))

println(listPair.first { it.second == 200 }) // (A, 200) 
println(listPair.last {it. second == 200 }) // (C, 200)
println(listPair.firstOrNull { it.first == "E" }) // null
println(listPair.lastOrNull { it.first == "E" }) // null
println(listPair.single { it.second == 100 }) // (B, 100)
println(listPair.singleOrNull {it.second == 200}) //null

3-6. 컬렉션의 분리와 병합

  • union()
    • 두 List 컬렉션을 병합하고 중복된 요소 값은 하나만 유지
    • Set 컬렉션을 반환
  • plus(), +연산자
    • 중복 요소를 포함해 합침
    • List 컬렉션을 반환
  • partition
    • 주어진 조건식의 결과(true, false)에 따라 List컬렉션을 두개로 분리
    • true에 해당하는 값은 첫번째 위치, false에 해당하는 값은 두번째 위치에 반환
    • Pair 로 반환
  • zip()
    • 2개의 컬렉션에서 동일한 인덱스끼리 Piar를 만들어 반환
    • 요소의 개수가 가장 적은 컬렉션에 맞춰 Pair가 구성
val list1 = listOf(1, 2, 3, 4, 5, 6)
val list2 = listOf(2, 2, 3, 4, 5, 5, 6, 7)

println(list1.union(list2)) // [1, 2, 3, 4, 5, 6, 7]
println(list1.plus(list2)) // [1,2,3,4,5,6,2,2,3,4,5,5,6,7]

println(list1.partition { it % 2 == 0 }) // [2, 4, 6], [1, 3, 5]

val zip = list1.zip(listOf(7,8)) // zip = [(1, 7), (2, 8)]

3-7. 순서와 정렬

  • reversed() : 요소의 순서를 거꾸로 반환
  • sorted()
    • 숫자 -> 작은 수에서 큰 수로 정렬 후 반환
    • 문자열 -> 알파벳 순으로 정렬 후 반환 (a → z)
  • sortedBy() : 특정한 비교식에 의해 정렬된 컬렉션 반환
  • sortedDescending, sortedByDescending
    • 숫자 -> 큰수에서 작은수로 정렬 후 반환
    • 문자열 -> 알파벳 반대 순으로 종렬 후 반환 (z → a)
val unsortedList = listOf(3, 2, 7, 5)

println(unsortedList.reversed()) // [5, 7, 2, 3]
println(unsortedList.sorted()) // [2, 3, 5, 7]
println(unsortedList.sortedDescending()) // [7, 5, 3, 2]
println(unsortedList.sortedBy { it % 3 }) // [3, 7, 2, 5]
println(unsortedList.sortedByDescending { it % 3 })  // [2, 5, 7, 3]

✏️4. 시퀀스 활용하기✏️

시퀀스(Sequence)란? 순차적인 컬렉션으로 요소의 크기를 특정하지 않고 나중에 결정할 수 있는 특수한 컬렉션


4-1. 요소 값 생성하기

📌 generateSequence()로 생성하기

  • generateSequence() : 시드(Seed) 인수에 의해 시작 요소의 값 결정
val nums: Sequence<Int> = generateSequence(1) { it + 1 }
println(nums.take(10).toList())
// nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

-> 시드 인수를 1로 지정하여 시작 요소의 값은 1
-> take(10)의 인자를 통해 원하는 개수만큼 요소가 저장
-> toList()를 통해 List 컬렉션으로 반환

📌 map, filter 연산 사용하기

  • map이나 filter와 같은 메서드를 연속하는 메서드 체이닝을 사용하면 하나의 구문이 끝날 때마다 중간 결과로 새로운 List를 계속해서 만들어냄
val squares = generateSequence(1) {it + 1}.map { it * it}
println(squares.take(10).toList()) 
// [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

val oddSquares = squares.filter {it % 2 != 0}
println(oddSquares.take(5).toList())
// [1, 9, 25, 49, 81]

4-2. 요소 값 가져오기

📌 메서드 체이닝의 중간 결과 생성하기

  • filtermap을 메서드 체이닝해서 사용할 경우 순차적 연산이기 때문에 시간이 많이 걸릴 수 있음
  • asSequence() : 병렬 처리 되기 때문에 속도나 메모리 측면에서 처리 성능이 좋아짐
val list1 = listOf(1, 2, 3)
val listDefault = list1
	.map { it * it } // [1, 4, 9]
    .filter { it % 2 == 0 } // [4]
/*
map(1)
map(2)
map(3)
filter(1)
filter(4)
filter(9)
[4]
*/

// asSequence() 사용
val listSeq = list1.asSequence()
	.map { it * it }
    .filter { it % 2 == 0 }
    .toList()
/* 병렬처리
map(1) filter(1)
map(2) filter(4)
map(3) filter(9)
[4]
*/

📍 작은 컬렉션에서는 시퀀스 사용을 권장하지 않음

  • filter() 등은 인라인 함수로 설계되어 있는데, 시퀀스를 사용하면 람다식을 저장하는 객체로 표현되기 때문에 인라인되지 않아 작은 컬렉션에서는 오히려 좋지 않음
  • 한번 계산된 내용은 메모리에 저장하기 때문에 시퀀스 자체를 인자로 넘기는 형태는 사용하지 않는 것이 좋음
profile
장똑대와 안드로이드

0개의 댓글

관련 채용 정보