본문 바로가기
코틀린/개발자를 위한 코틀린 프로그래밍

[chapter08] 컬렉션 알아보기

by 측면삼각근 2023. 8. 31.
728x90
반응형

들어가기 전에

본 포스팅은 개발자를 위한 코틀린 프로그래밍의 chapter단위로 공부하고, 정리, 부족한 내용의 추가 학습내용을 정리하는 블로깅입니다.

이전 포스팅 ⬇️

[chapter07] 클래스 관계 등 추가사항 알아보기

 

[chapter07] 클래스 관계 등 추가사항 알아보기

들어가기 전에 본 포스팅은 개발자를 위한 코틀린 프로그래밍의 chapter단위로 공부하고, 정리, 부족한 내용의 추가 학습내용을 정리하는 블로깅입니다. 이전 포스팅 ⬇️ [chapter06] 내장 자료형

messycode.tistory.com


01 리스트, 세트, 맵 알아보기

1.1 컬렉션의 가변(mutable)과 불변(immutable)

변수도 가변과 불변이 있듯이 객체에도 가변과 불변이 있다. 그래서 컬렉션의 가변과 불변은 내부 원소의 추가, 변경, 삭제 가능 여부를 처리한다.

  • 가변(mutable): 보통 컬렉션 객체는 추가, 수정, 삭제를 할 수 있다.
  • 불변(immutable): 컬렉션 객체 중에 한 번 만들어지면 추가, 수정, 삭제를 할 수 없다. 하지만 불변 객체의 내부 원소에 대한 추가, 삭제, 수정 메서드를 가지면 기존 객체는 그대로 두고 새로운 객체를 만든다.

1.2 리스트 클래스

리스트는 아래 두 가지로 만들어진다.

  • ArrayList: 고정 길이로 리스트를 만든다. 고정 길이가 넘을 경우 리스트의 사이즈를 확대해서 원소를 추가한다.
    보통 listOf, mutableListOf로 객체를 생성한다.
  • LinkedList: 가변길이로 리스트를 만든다. 각 원소가 가지는 주소를 별도로 보관하므로 링크드리스트라고 한다.
    보통 LinkedList 클래스로 리스트 객체를 생성한다.
import java.util.*

fun List<Int>.dir():Set<String>{
    return this.javaClass.kotlin.members.map { it.name }.toSet()
}

//fun AbstractSequentialList<String>.dir():Set<String>{
//    return this.javaClass.kotlin.members.map { it.name }.toSet()
//}

fun main() {
    // 리스트 객체 생성 (불변 리스트)
    val list = listOf(1, 2, 3, 4, 5)
    println(list.javaClass.kotlin) // class java.util.Arrays$ArrayList
    println(list.javaClass.kotlin.supertypes[0]) // java.util.AbstractList<E>
    val listDir = list.dir()

    val mutableList = mutableListOf(1, 2, 3, 4, 5)
    println(mutableList.javaClass.kotlin) // class java.util.ArrayList
    println(mutableList.javaClass.kotlin.supertypes[0]) // java.util.AbstractList<E>
    val mutableListDir = mutableList.dir()

    println("===list와 mutableList의 동일한 메소드===\n" +
            "${listDir.intersect(mutableListDir)}" +
            "\n====================================\n")
    // [iterator, contains, spliterator, forEach, toArray, set, get, indexOf, replaceAll,
    // sort, size, add, addAll, clear, remove, removeAll, retainAll, containsAll, isEmpty,
    // equals, hashCode, toString, stream, removeIf, parallelStream, listIterator, removeAt,
    // subList, lastIndexOf, removeRange, modCount, serialVersionUID, subListRangeCheck]

    println("===mutableList에만 있는 메서드===\n" +
            "${mutableListDir.minus(listDir)}" +
            "\n====================================\n")
    // [rangeCheckForAdd, outOfBoundsMsg, clone, readObject, writeObject, ensureCapacity,
    // trimToSize, elementData, grow, indexOfRange, lastIndexOfRange, fastRemove, equalsArrayList,
    // equalsRange, checkForComodification, hashCodeRange, shiftTailOverGap, batchRemove, replaceAllRange,
    // checkInvariants, elementAt, nBits, setBit, isClear, DEFAULT_CAPACITY,
    // EMPTY_ELEMENTDATA, DEFAULTCAPACITY_EMPTY_ELEMENTDATA]

    // 링크드 리스트 객체 생성
    val linkedList = LinkedList<String>()
    println(linkedList.javaClass.kotlin) // class java.util.LinkedList
    println(linkedList.javaClass.kotlin.supertypes[0]) // java.util.AbstractSequentialList<E>
}

다양한 리스트 객체를 생성하는 법

가변리스트와 불변리스트

fun main() {
    val emptyList: List<String> = emptyList() // 아무것도 없는 리스트 생성
    println(emptyList.javaClass.kotlin) // class kotlin.collections.EmptyList
    println(emptyList.javaClass.kotlin.supertypes[0]) // kotlin.collections.List<kotlin.Nothing>

    // listOfNotNull() 함수는 null이 아닌 요소만으로 리스트를 생성한다.
    val nonNullList = listOfNotNull("Hello", null, "World")
    println(nonNullList) // [Hello, World]
    println(nonNullList.javaClass.kotlin) // class java.util.ArrayList
    println(nonNullList.javaClass.kotlin.supertypes[0]) // java.util.AbstractList<E!>

    // arrayListOf() 함수는 가변형 리스트를 생성한다.
    val strList = arrayListOf<String>("Hello", "World")
    println(strList)
    println(strList.javaClass.kotlin) // class java.util.ArrayList

    // listOf() 함수는 불변형 리스트를 생성한다.
    val strList2: List<String> = listOf("hello", "world")
    println(strList2.javaClass.kotlin) // class java.util.Arrays$ArrayList

    // 가변 리스트는 mutableList() 함수로 생성한다.
    val mutableList: MutableList<String> = mutableListOf("hello", "world")
    // 가변 리스트는 원소를 추가하거나 삭제가 가능하다.
    mutableList.add("!")
    println(mutableList) // [hello, world, !]

    // 불변 리스트를 2개 만들어서 더하면 새로운 리스트가 생성된다. (두 불변 리스트는 변경이 없다.)
    val list1 = listOf("hello", "world")
    val list2 = listOf("!")
    val list3 = list1 + list2
    println(list3) // [hello, world, !]
}

1.3 집합(Set)클래스

Set은 순서가 없고 모든 원소는 유일한 값을 가진다. 집합에서 가변과 불변 클래스가 별도로 제공해서 처리할 수 있다.

집합의 종류

 집합의 원소는 유일한 값을 처리하기 위해 hash 구성하는 값을 가지고 있다. 그래서 집합의 종류도 해시를 기반으로 처리한다.

  • HashSet: HashMap을 이용하여 구현되고, value는 dummy object로 사용된다.
  • LinkedHashSet: LinkedHashMap을 이용하여 구현되고, value는 dummy object로 사용된다.
  • TreeSet: TreeMap으로 구현되고, value는 dummy object로 사용된다.

코틀린에서는 집합도 리스트처럼 가변과 불변은 컴파일 타입에서만 처리한다.
리플렉션으로 가져온 클래스의 멤버는 가변이나 불변을 구분하지 않는다.

import java.util.*

fun Set<String>.dir(): Set<String> {
    return this.javaClass.kotlin.members.map { it.name }.toSet()
}

fun main() {
    val hashSet = hashSetOf<String>("a", "b", "c")
    val hashSetDir = hashSet.dir()

    val treeSet = TreeSet<String>()
    val treeSetDir = treeSet.dir()

    // 두 개의 Set이 동일한 dir() 함수
    println(hashSetDir.intersect(treeSetDir))
    /*
    [
    add, clear, iterator, remove, contains, isEmpty, spliterator,
    toArray, clone, readObject, writeObject, size, addAll, removeAll,
    retainAll, containsAll, equals, hashCode, toString, forEach, stream,
    removeIf, parallelStream, serialVersionUID, PRESENT
    ]
    * */

}

변수 자료형 지정 방식

val mixedTypesSet: Set<Any> = setOf(1, "two", 3.0, "four")
val intSet: Set<Int> = setOf(1, 2, 3)

val mixedMutableSet: MutableSet<Any> = mutableSetOf(1, "two", 3.0, "four")
val intMutableSet: MutableSet<Int> = mutableSetOf(1, 2, 3)

// 더하기 때기 연산자로 원소를 추가, 삭제 할 수 있다.
mixedMutableSet += 1
mixedMutableSet -= 1

원소 추가 삭제

  • 가변 집합(hashSetOf, mutableSetOf, linkedSetOf)을 만들고 메서드를 사용해 원소를 추가하거나 삭제할 수 있다.
    • add, remove, clear
  • TreeSet은 클래스로 빈 집합을 만든다. 그다음 addAll메서드로 집합을 추가한다.
val hashSet = hashSetOf<Int>(1, 2, 3, 4)
    hashSet.add(5)
    hashSet.remove(1)
    println(hashSet) // [2, 3, 4, 5]
    // mutableSetOf() is an alias for hashSetOf()
    // linkedSetOf() is an alias for linkedHashSetOf()

    val treeSet = TreeSet<Int>()
    treeSet.addAll(listOf(1, 2, 3, 4))

집합 기본 연산

수학의 집합처럼 코틀린 집합도 집한 연산을 처리할 수 있다.

    val list = listOf<String>("a", "b", "c", "c")
    println(list.toSet()) // 리스트를 Set으로 변환 => [ a, b, c ]
    println(list.distinct()) // 리스트에서 중복을 제거한 리스트를 반환 => [ a, b, c ]
    println(list.joinToString("").toSet()) // 문자열을 Set으로 변환 => [ a, b, c ]

    val intSet = setOf<Int>(1, 1, 3, 4, 9, 9, 4)
    println(intSet) // [ 1, 3, 4, 9 ] 중복을 제거한 Set을 반환

    // 원소 여부 확인
    println(intSet.contains(1)) // true
    println(1 in intSet) // true
    println(intSet.containsAll(listOf(1, 3, 4))) // true
    
    // 합연산
    println(intSet.union(setOf(1, 2, 3))) // [ 1, 2, 3, 4, 9 ]
    println(intSet + setOf(1, 2, 3)) // [ 1, 2, 3, 4, 9 ]
    // 차연산 (차집합)
    println(intSet.subtract(setOf(1, 2, 3))) // [ 4, 9 ]
    println(intSet - setOf(1, 2, 3)) // [ 4, 9 ]
    // 교집합
    println(intSet.intersect(setOf(1, 2, 3))) // [ 1, 3 ]
    println(intSet intersect setOf(1, 2, 3)) // [ 1, 3 ]

1.4 맵 클래스

맵은 유일한 값을 갖는 키와 이 키에 대응하는 값으로 구성된다. 키를 만들 수 있는 객체는 문자열 등의 불변 객체만 올 수 있다. 하지만 값으로는 모든 객체를 다 저장할 수 있다.

맵의 종류

맵도 해시는 기본이지만 트리 자료구조로 구성할 수 있다.

  • TreeMap: Tree 구조를 사용해서 만든 맵
  • HashMap: Hash기법을 사용해서 만든 맵
  • LinkedHashMap: Hash기법과 만들어진 키를 연결해서 만든 맵. HashMap과 다른 점은 기본적으로 데이터를 추가한 순서대로 데이터를 보관한다.
// HashMap은 순서가 보장되지 않는다.
val hashMap = hashMapOf<String, String>(Pair("a", "b"), Pair("c", "d"))
// 원소 추가
hashMap.put("e", "f")
// 원소 삭제
hashMap.remove("a")
// 원소 수정
hashMap.replace("c", "d", "D") // c의 값이 d이면 D로 바꿔라
// 원소 조회
hashMap.get("c")

// linkedMap은 순서가 보장된다.
val linkedMap = linkedMapOf<String, String>("1" to "one", "2" to "two")
println(linkedMap.javaClass.simpleName) // LinkedHashMap

1.5 스택(Stack)

스택은 하나의 출입구만 있으므로 차례대로 스택에 쌓고 마지막에 쌓인 것을 꺼낸다.

  • push: 스택을 쌓는다.
  • pop: 스택에서 제거
  • peek: 스택의 최상단에 있는 데이터를 알려준다.

코틀린에서는 별도의 스택을 사용하지는 않지만, 자바에 있는 스택을 활용할 수 있다.

import java.util.*

val stack = Stack<Char>()
println(stack.empty()) // true

stack.push('a')
stack.push('b')
stack.push('c')
println(stack) // [a, b, c]

println(stack.peek()) // c
println(stack.search('b')) // 2 => 인덱스는 1부터 시작


// 자바 deque로도 stack을 구현할 수 있다. (덱은 양쪽 끝에서 삽입과 삭제가 모두 가능한 자료구조)
val deque = ArrayDeque<Char>()
deque.push('a')
deque.push('b')
deque.push('c')
println(deque) // [c, b, a]
println(deque.pop()) // c
println(deque) // [b, a]

코틀린 확장함수로 처리

typealias Stack<T> = MutableList<T>

fun main() {
    fun <T> Stack<T>.push(item: T) = add(item)
    fun <T> Stack<T>.pop(): T? = if (size > 0) removeAt(size - 1) else null
    fun <T> Stack<T>.peek(): T? = getOrNull(size - 1)

    val stack: Stack<Int> = mutableListOf<Int>(1, 2, 3)
    println(stack.isEmpty()) // false
    println(stack.isNotEmpty()) // true
}

1.6 큐(queue)

자바의 큐 인터페이스와 링크드리스트, Arry Deque사용

import java.util.Queue
import java.util.LinkedList
import java.util.ArrayDeque

fun main() {
    // 자바 큐 인터테이스와 링크드 리스트 사용
    val linkedListQueue: Queue<String> = LinkedList<String>()
    linkedListQueue.add("A")
    linkedListQueue.add("B")
    println(linkedListQueue) // [A, B]
    println(linkedListQueue.peek()) // A
    println(linkedListQueue.size) // 2

    println(linkedListQueue.poll()) // A
    println(linkedListQueue.indexOf("B")) // 0
    println(linkedListQueue.contains("B")) // true

    // 자바 ArrayDeque 사용
    val arrayDeque: Queue<String> = ArrayDeque<String>()
    arrayDeque.add("A")
    arrayDeque.add("B")
    println(arrayDeque) // [A, B]
    println(arrayDeque.peek()) // A
    println(arrayDeque.first()) // A

}

큐를 만들려면 특정 기능과 데이터를 관리하는 곳을 분리한다. 보통 데이터 관리 속성은 비공개 처리하고 특정 기능은 인터페이스를 정의해서 큐를 처리하는 클래스에서 이 인터페이스를 구현처리한다.

컬렉션 메서드 알아보기

2.1 컬렉션 상속구조 알아보기

 2.2 검색과 조건 검사

list와 map, set의 선언과 순환, 접근에 대해 알아본다.

val list = listOf<String>("a", "b", "c")
// size => 3 , isEmpty => false, contains("b") => true, containAll(listOf("a", "b")) => true

val set = setOf<String>("a", "b", "c")
// size => 3 , isEmpty => false, contains("b") => true, containAll(listOf("a", "b")) => true

val map = mapOf<String, Int>("100" to 100, "200" to 200)
// size => 2 , isEmpty => false, contains("b") => true, containsKey("100") => true, containsValue(100) => true

// 컬렉션 내부 순환
list.forEach(::println)
list.forEachIndexed { index, s -> println("$index : $s") } // 인덱스와 함께 순환
map.forEach { (key, value) -> println("$key : $value") } // Map의 경우 key, value를 함께 순환

// 리스트와 Set원소 조회
list.first() // 첫번째 원소
list.last() // 마지막 원소
list.elementAt(1) // 1번째 원소
list.indexOf("b") // "b"의 인덱스 => 값이 없다면 -1
list.lastIndexOf("b") // "b"의 마지막 인덱스 => 값이 없다면 -1

// 검색
list.find { it.startsWith("a") } // "a"로 시작하는 첫번째 원소
// 최대값, 최소값
list.max() // 가장 큰 값
list.min() // 가장 작은 값

set.first() // 첫번째 원소
set.last() // 마지막 원소
set.elementAt(1) // 1번째 원소
set.indexOf("b") // "b"의 인덱스 => 값이 없다면 -1

// 맵 원소 접근
list.zip(set).toTypedArray() // 리스트와 셋을 합쳐서 새로운 리스트를 만듬, zip은 두 컬렉션의 원소를 1:1로 매칭
map.getOrElse("100") { 0 } // "100"의 값이 없다면 0을 반환
// 최대값 최소값
map.maxBy { it.value } // value의 최대값
map.minBy { it.value } // value의 최소값

조건 검사

val list = listOf<String>("a", "ab", "abc")
val set = setOf<String>("a", "b", "c")
val map = mapOf<String, Int>("100" to 100, "200" to 200)

list.any { it.length == 1 } // true => any는 하나라도 만족하면 true
list.all { it.length == 1 } // false => all은 모두 만족해야 true
list.none { it.length == 1 } // false => none은 모두 만족하지 않아야 true

map.any { it.key.length == 3 } // true => map은 key, value로 이루어져 있으므로 it.key로 key에 접근 가능

2.3 정렬, 삭제, 조인처리

정렬

val mutableList = mutableListOf<String>("b", "c", "a")

// 정렬
mutableList.sort() // Unit 반환
println(mutableList) // [a, b, c]

// 역순 정렬
val reversedList = mutableList.reversed() // List 반환
println("reversedList: $reversedList") // [c, b, a]
println("mutableList:$mutableList") // [a, b, c]

// 역순 정렬 후 원본 변경
mutableList.reverse() // Unit 반환
println("mutableList: $mutableList") // [c, b, a]

// => reversed 원본을 변경하지 않음, reverse 원본을 변경함

drop, take (list, set)

  • drop메서드는 특정 정수 즉 인덱스 이후 삭제 처리한다. (원본은 그대로 유지)
  • drop while은 람다표현식을 받아서, 해당 조건을 만족하면 삭제한다. (원본 유지)
  • dropLast메서드는 우측에서 정수 개수만큼 삭제한다.
val list = listOf(1, 2, 3, 4, 5)
list.drop(3) // 정수 갯수만큼 삭제 후 반환 => [4, 5]
list.dropWhile { it < 3 } // 조건에 맞는 요소 삭제 후 반환 => [3, 4, 5]
list.dropLast(3) // 뒤에서부터 정수 갯수만큼 삭제 => [1, 2]
list.dropLastWhile { it > 3 } // 뒤에서부터 조건에 맞는 요소 삭제 => [1, 2, 3]

// take로 원소를 꺼내기
list.take(3) // 정수 갯수만큼 반환 => [1, 2, 3]
list.takeWhile { it < 3 } // 조건에 맞는 요소 반환 => [1, 2]
list.takeLast(3) // 뒤에서부터 정수 갯수만큼 반환 => [3, 4, 5]
list.takeLastWhile { it > 3 } // 뒤에서부터 조건에 맞는 요소 반환 => [4, 5]

조인해서 문자열 처리하기

val list = listOf(1, 2, 3, 4, 5)
println(list) // [1, 2, 3, 4, 5]
println("일반 출력: " + list.joinToString { "\"$it\"" }) // 일반 출력: "1", "2", "3", "4", "5"
println("포맷 출력: " + list.joinToString(", ", "{", "}")) // 포맷 출력: {1, 2, 3, 4, 5}
println("포맷 출력: " + list.joinToString(prefix = "[", postfix = "]")) // 포맷 출력: [1, 2, 3, 4, 5]

val sb = StringBuilder("The list of numbers: ")
list.joinTo(sb, prefix = "[", postfix = "]")
println(sb) // The list of numbers: [1, 2, 3, 4, 5]
// StringBuilder 사용할때는 joinTo를 사용한다.
// buffer: A, separator: CharSequence = ", ", prefix: CharSequence = "", postfix: CharSequence = "", limit: Int = -1, truncated: CharSequence = "...", transform: ((T) -> CharSequence)? = null

중복 데이터 없애기

// 중복 제거하기
val list = listOf(1, 2, 3, 4, 5, 5, 5, 6, 7, 8)
list.distinct() // [1, 2, 3, 4, 5, 6, 7, 8]
println(list) // [1, 2, 3, 4, 5, 5, 5, 6, 7, 8] -> 원본은 그대로

// 중복 제거하기 2
list.distinctBy { it % 3 } // [1, 2, 3]

// 중복 제거하기 3
val set = list.toSet() // [1, 2, 3, 4, 5, 6, 7, 8]

2.4 맵 리듀스 처리

컬렉션은 내부순환을 처리하는 여러 메서드를 제공한다.

맵 리듀스

컬렉션은 여러 원소를 가진다. 원소를 처리하기 위해 순환 등의 기능을 하는 메서드를 만들고 실제 특정 처리는 별도의 함수를 전달받아서 한다. 그래서 다양한 기능을 적용할 수 있는 방식을 제공한다.

  • 맵(map:) 컬렉션을 순환. 모든 원소를 변형하여 컬렉션을 반환
  • 필터(filter): 컬렉션을 순환, 전달된 조건을 점검해서 true인 원소만 추출, 반환
  • 리듀스(reduce): 컬렉션을 순환, 모든 원소를 람다 표현식으로 연산해서 최종 결과를 일반적인 자료 형 값으로 반환
  •  폴드(fold): 리듀스와 동일하지만 초기값 인자를 더 받아서 초깃값부터 람다표현식으로 연산한다.
// map, filter, reduce, fold
val list = listOf(1, 2, 3, 4, 5, 6, 7, 8)
val list2 = list.map { it * 2 } // [2, 4, 6, 8, 10, 12, 14, 16]
val list3 = list.filter { it % 2 == 0 } // [2, 4, 6, 8]

// 하나의 값으로 처리
val list4 = list.reduce { acc, i -> acc + i } // 36
// fold는 초기값이 따로 필요하다
val list5 = list.fold(0) { acc, i -> acc + i } // 36

// 클래스로 활용도 가능하다.
data class Animal(val name: String, val species: String)

val animals = listOf(
    Animal("Fido", "Dog"),
    Animal("Felix", "Cat"),
    Animal("Rover", "Dog"),
    Animal("Isis", "Cat"),
    Animal("Spot", "Dog"),
    Animal("Sylvester", "Cat")
)
val dogs = animals.filter { it.species == "Dog" }
val cats = animals.filter { it.species == "Cat" }
animals.filter { it.species == "Dog" }.map { it.name }
// [Fido, Rover, Spot] -> 메서드 체이닝이 가능하다.

2.5 그룹 연산

컬렉션을 처리할 때, 특정 원소를 기준으로 동일한 범주로 묶어서 할 수 있다. 이렇게 특정 값으로 묶을 때는 Map으로 반환하는 groupBy메서드로 그룹화하고, 바로 연산이 필요할 때는 groupingBy를 사용하면 된다.

// 그룹연산: groupBy
val list = listOf(1, 2, 3, 4, 5, 6)
val map = list.groupBy { if (it % 2 == 0) "even" else "odd" }
println(map)
// {odd=[1, 3, 5], even=[2, 4, 6]}

// 그룹연산: groupingBy => 그룹화하고 바로 집계
val map2 = list
    .groupingBy { if (it % 2 == 0) "even" else "odd" }
    .eachCount()
println(map2)
// {odd=3, even=3}

// 데이터 클래스 객체를 그룹화
data class Person(val name: String, val age: Int)

val people = listOf(
    Person("Alice", 21),
    Person("Bob", 25),
    Person("Carol", 25),
    Person("Dan", 21)
)
val ageToPeople = people.groupBy { it.age }
println(ageToPeople)
// {21=[Person(name=Alice, age=21), Person(name=Dan, age=21)],
// 25=[Person(name=Bob, age=25), Person(name=Carol, age=25)]}

// groupByTo => 첫번째 인자에 전달한 Map에 그룹화 결과를 저장
val group = people.groupByTo(mutableMapOf()) { it.age }
println(group)
// {21=[Person(name=Alice, age=21), Person(name=Dan, age=21)],
// 25=[Person(name=Bob, age=25), Person(name=Carol, age=25)]}

2.6 시퀀스(Sequence)

컬렉션은 두 가지 방법으로 실행한다.

  • 즉시 실행(Eager evaluation)
  • 지연 실행(Lazy evaluation)

앞에서는 대부분 즉시실행이었는데, 지연 처리하는 방식인 시퀀스(Sequence)를 알아보자
지연 처리의 기준은 특정 액션을 만나면 그때 모든 것을 처리한다.

시퀀스의 기본

시퀀스를 생성하는 함수가 있고, 기존 컬렉션을 시퀀스로 변환해서 처리할 수 있다. 또한 무한 처리가 가능한 시퀀스 함수도 있다.

// 시퀀스
val seq = sequenceOf(1, 2, 3, 4, 5)
seq.forEach { print("$it ") }
println(seq.count()) // 5

val nums = generateSequence(1) { it + 1 } // 초기값 1, 다음 값은 이전 값에 1을 더한 값
println(nums.take(10).toList()) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
println(nums.count()) // 무한대 (정지 조건이 없기 때문에)

시퀀스와 반복자 함수로 시퀀스 처리

시퀀스를 만드는 함수인 sequence와 iterator를 제공한다.

val seq = sequence {
    println("반복")
    yield(1) // 한번 실행
    yieldAll(listOf(2, 3)) // 리스트 원소만큼 실행
    yieldAll(generateSequence(4) { it + 1 }) // 무한 반복
}

println(seq.take(5).toList()) // [1, 2, 3, 4, 5]

val seq2 = iterator {
    println("반복")
    yield(42)
    yieldAll(1..4 step 2)
}
while (seq2.hasNext()) {
    print("${seq2.next()}, ")
}

일반 컬렉션과 시퀀스 처리방식 비교

일반적인 컬렉션은 정적 처리이며, 시퀀스는 동적 처리이다.

반응형