Search

Overview

Kotlin 표준 라이브러리는 문제 해결에 중요하고 일반적으로 많이 다뤄지는 컬렉션을 관리하기 위한 포괄적인 도구들을 제공한다.
Kotlin의 컬렉션은 Java 또는 Python 과 같은 대부분의 프로그래밍 언어의 컬렉션과 비슷한 일반적인 개념이다.

컬렉션

컬렉션은 일반적으로 같은 타입(또는 하위 타입)의 여러 객체를 포함한다.
컬렉션 내 객체는 요소 또는 항목이라고한다.
Kotlin은 저장된 객체의 타입에 상관없이 동일하게 컬렉션을 조작할 수 있게 해준다.
Kotlin 표준 라이브러리는 모든 타입의 컬렉션을 생성, 채우기, 관리할 수 있는 제네릭 인터페이스, 클래스 및 함수를 제공한다.
컬렉션 인터페이스 및 관련 함수들은 kotlin.collections 패키지에 위치한다.
배열은 컬렉션 타입이 아니다.

컬렉션 타입

컬렉션 타입은 두 개의 인터페이스로 나뉜다.
컬렉션 요소에 접근하는 작업을 제공하는 읽기 전용 인터페이스
읽기 전용 인터페이스를 확장하여 요소 추가, 삭제, 업데이트와 같은 쓰기 작업을 제공하는 가변 인터페이스
val numbers = mutableListOf("one", "two", "three", "four") numbers.add("five") // 가능 println(numbers) //numbers = mutableListOf("six", "seven") // 컴파일 오류
Kotlin
복사
가변 컬렉션이 반드시 var로 할당될 필요는 없고 val로 할당되더라도 쓰기 작업은 가능하다.
가변 컬렉션을 val로 할당하는 이점은 컬렉션 참조가 수정되지 않도록 보호할 수 있다는 점이다.
코드가 복잡해짐에 따라 참조의 비의도적인 수정으로부터 보호하는 것이 중요해진다.
가능한 한 val을 사용하면 더 안전하고 견고한 코드를 작성할 수 있다.
읽기 전용 컬렉션 타입
공변성을 가진다.
만약 Rectangle 클래스가 Shape를 상속받는다면 List<Rectangle>은 List<Shape>가 필요한 곳에서 사용할 수 있음을 의미한다.
즉, 컬렉션 타입도 요소 타입과 동일한 서브타입 관계를 가진다.
Map은 Value 타입에서 공변성이 있지만 Key 타입에서는 그렇지 않다.
가변 컬렉션 타입
공변성을 가지지 않는다.
만약 MutableList<Rectangle>이 MutableList<Shape>의 서브타입이라면 다른 Shape 상속자를 삽입할 수 있어 Rectangle 타입 인수를 위반하게 된다.
즉, 런타임 오류가 발생할 수 있다.

Collection

fun printAll(strings: Collection<String>) { for(s in strings) print("$s ") println() } fun main() { val stringList = listOf("one", "two", "one") printAll(stringList) val stringSet = setOf("one", "two", "three") printAll(stringSet) }
Kotlin
복사
Collection<T>은 컬렉션 계층의 루트이다.
이 인터페이스는 읽기 전용 컬렉션의 공통 동작(크기 조회, 항목 포함 여부 확인 등)을 나타낸다.
Collection은 요소를 반복하는 작업을 정의하는 Iterable<T> 인터페이스를 상속한다.
Collection을 사용하여 다양한 컬렉션 타입에 적용되는 함수를 정의할 수 있다.
더 구체적인 경우에는 Collection의 하위 타입인 List 와 Set 을 사용하라.
fun List<String>.getShortWordsTo(shortWords: MutableList<String>, maxLength: Int) { this.filterTo(shortWords) { it.length <= maxLength } val articles = setOf("a", "A", "an", "An", "the", "The") shortWords -= articles } fun main() { val words = "A long time ago in a galaxy far far away".split(" ") val shortWords = mutableListOf<String>() words.getShortWordsTo(shortWords, 3) println(shortWords) }
Kotlin
복사
MutableCollection<T>은 쓰기 작업(add, remove)을 지원하는 Collection이다.

List

val numbers = listOf("one", "two", "three", "four") println("Number of elements: ${numbers.size}") println("Third element: ${numbers.get(2)}") println("Fourth element: ${numbers[3]}") println("Index of element \"two\": ${numbers.indexOf("two")}")
Kotlin
복사
List<T>요소를 특정 순서로 저장하고 인덱스를 통해 접근할 수 있게 한다.
인덱스는 0부터 시작하며 마지막 인덱스는 (list.size - 1) 이다.
val bob = Person("Bob", 31) val people = listOf(Person("Adam", 20), bob, bob) val people2 = listOf(Person("Adam", 20), Person("Bob", 31), bob) println(people == people2) bob.age = 32 println(people == people2)
Kotlin
복사
List의 요소(null 포함)는 중복될 수 있다.
List는 동일한 객체나 하나의 객체를 여러 번 포함할 수 있다.
두 리스트는 크기가 같고 같은 위치에 구조적으로 동일한 요소가 있으면 동일한 것으로 간주된다.
val numbers = mutableListOf(1, 2, 3, 4) numbers.add(5) numbers.removeAt(1) numbers[0] = 0 numbers.shuffle() println(numbers)
Kotlin
복사
MutableList<T>는 List 전용 쓰기 작업(특정 위치에 요소 추가, 삭제)을 제공하는 List이다.
List는 Array와 매우 유사하지만 중요한 차이가 있다. Array의 크기는 초기화 시 정의되며 변경되지 않지만 List는 크기가 고정되지 않으며 변경될 수 있다. Kotlin에서 MutableList의 기본 구현은 ArrayList이며 크기를 조절할 수 있는 배열이다.

Set

val numbers = setOf(1, 2, 3, 4) println("Number of elements: ${numbers.size}") if (numbers.contains(1)) println("1 is in the set") val numbersBackwards = setOf(4, 3, 2, 1) println("The sets are equal: ${numbers == numbersBackwards}")
Kotlin
복사
Set<T>중복되지 않는 요소를 저장하며 요소의 순서는 일반적으로 정의되지 않는다.
null 요소 역시 중복되지 않으며 Set에는 null이 한 번만 포함될 수 있다.
val numbers = setOf(1, 2, 3, 4) val numbersBackwards = setOf(4, 3, 2, 1) println(numbers.first() == numbersBackwards.first()) println(numbers.first() == numbersBackwards.last())
Kotlin
복사
MutableSet은 MutableCollection에서 제공하는 쓰기 작업을 가진 집합이다.
기본 구현체인 LinkedHashSet은 요소 삽입 순서를 유지하므로 first() 나 last() 같은 순서에 의존하는 함수는 예측 가능한 결과를 반환한다. 대안 구현체인 HashSet은 요소의 순서에 대해 아무것도 보장하지 않으며 순서에 의존하는 함수 호출 시 결과가 예측 불가하지만 더 적은 메모리를 사용한다.

Map

val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key4" to 1) println("모든 키: ${numbersMap.keys}") println("모든 값: ${numbersMap.values}") if ("key2" in numbersMap) println("key2의 값: ${numbersMap["key2"]}") if (1 in numbersMap.values) println("값 1이 맵에 있습니다.") if (numbersMap.containsValue(1)) println("값 1이 맵에 있습니다.") // 위와 동일
Kotlin
복사
Map<K, V>는 Collection 인터페이스의 상속자가 아니지만 Kotlin 컬렉션 타입 중 하나이다.
Map은 Key-Value 쌍(또는 Entry)을 저장하며 Key는 고유해야 하지만 서로 다른 Key가 동일한 Value를 가질 수 있다.
Map 인터페이스는 Key로 Value를 접근하거나 Key와 Value를 검색하는 등의 특정 기능을 제공한다.
val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key4" to 1) val anotherMap = mapOf("key2" to 2, "key1" to 1, "key4" to 1, "key3" to 3) println("맵은 동일한가요? ${numbersMap == anotherMap}")
Kotlin
복사
두 Map이 동일한 쌍을 가지고 있다면 순서에 관계없이 동일한 것으로 간주된다.
val numbersMap = mutableMapOf("one" to 1, "two" to 2) numbersMap.put("three", 3) numbersMap["one"] = 11 println(numbersMap)
Kotlin
복사
MutableMap은 Map에 쓰기 작업이 가능한 변형이다.
새로운 Key-Value 쌍을 추가하거나 주어진 Key에 연관된 Value를 업데이트할 수 있다.
기본 구현체인 LinkedHashMap은 요소 삽입 순서를 보존한다. 대안 구현체인 HashMap은 요소의 순서에 대한 보장이 없다.

ArrayDeque

fun main() { val deque = ArrayDeque(listOf(1, 2, 3)) deque.addFirst(0) deque.addLast(4) println(deque) // [0, 1, 2, 3, 4] println(deque.first()) // 0 println(deque.last()) // 4 deque.removeFirst() deque.removeLast() println(deque) // [1, 2, 3] }
Kotlin
복사
ArrayDeque<T>양방향 큐의 구현체로 큐의 앞과 뒤에서 요소를 추가하거나 제거할 수 있다.
ArrayDeque는 Kotlin에서 스택과 큐 모두의 역할을 할 수 있다.
내부적으로는 크기가 자동으로 조정하는 배열을 사용하여 구현된다.