HOME > kotlin > basic

Kotlin - Collection 소개 및 사용법 정리

JSFollow12 Jul 2019

Collection(콜렉션)은 대부분의 프로그래밍 언어에 있는 자료구조입니다. 자바의 List, Map, Set 등을 Collection이라고 합니다. Collection은 Generic으로 구현이 되어 다양한 타입과 함께 사용될 수 있습니다.

코틀린의 Collection은 기본적으로 Mutable(변할 수 없는)과 Immutable(불변의)을 별개로 지원합니다. Mutable로 생성하면 추가, 삭제가 가능하지만, Immutable로 생성하면 수정이 안됩니다.

코틀린의 콜렉션들은 아래 그림과 같은 상속 구조 갖고 있습니다. collections diagram (출처: kotlinlang.com)

이 글에서는 콜렉션을 한번씩 사용해보았다고 가정하고, 코틀린에서 콜렉션이 어떻게 사용되는지 예제와 함께 알아보겠습니다.

List

List는 데이터가 저장하거나 삭제될 때 순서를 지키는 Collection입니다. List는 Mutable(변할 수 없는)과 Immutable(불변의)을 모두 지원합니다.

Immutable(수정할 수 없는 객체, 불변의)

listOf<타입>(아이템, )로 Immutable List를 생성 및 초기화를 할 수 있습니다. 코틀린은 아이템의 타입을 추론하기 때문에 타입을 생략해도 됩니다. Immutable이기 때문에 get만 가능합니다. List의 getter는 자바처럼 get(index)도 지원하고 배열처럼 [index]도 지원합니다. 코틀린은 간결하고 직관적인 배열같은 표현 방식을 선호합니다.

val fruits= listOf<String>("apple", "banana", "kiwi", "peach")
// val fruits= listOf("apple", "banana", "kiwi", "peach") -> 타입 생략 가능
println("fruits.size: ${fruits.size}")
println("fruits.get(2): ${fruits.get(2)}")
println("fruits[3]: ${fruits[3]}")
println("fruits.indexOf(\"peach\"): ${fruits.indexOf("peach")}")

위의 코드를 실행한 결과입니다.

fruits.size: 4
fruits.get(2): kiwi
fruits[3]: peach
fruits.indexOf("peach"): 3

Mutable(수정가능한 객체, 변할 수 있는)

수정가능한 List는 mutableListOf로 선언합니다. listOf와 대부분 비슷하지만, 추가 및 삭제가 가능합니다. 자바의 Collection에 익숙하시다면 remove, add, addAll, removeAt 등은 이미 알고 계실 것입니다.

val fruits= mutableListOf<String>("apple", "banana", "kiwi", "peach")
fruits.remove("apple")
fruits.add("grape")
println("fruits: $fruits")

fruits.addAll(listOf("melon", "cherry"))
println("fruits: $fruits")
fruits.removeAt(3)
println("fruits: $fruits")

위의 코드를 실행한 결과입니다.

fruits: [banana, kiwi, peach, grape]
fruits: [banana, kiwi, peach, grape, melon, cherry]
fruits: [banana, kiwi, peach, melon, cherry]

그 외에도 replace, replaceAll, contains, forEach 등의 메소드도 지원합니다.

Set

Set은 동일한 아이템이 없는 Collection입니다. Set의 아이템들의 순서는 특별히 정해져 있지 않습니다. Set은 null 객체를 갖고 있을 수 있습니다. 동일한 객체는 추가될 수 없기 때문에 null도 1개만 갖고 있을 수 있습니다. List와 같이 Set도 Immutable과 Mutable을 별개로 지원합니다.

Immutable

setOf<타입>(아이템들)로 객체를 생성할 수 있습니다. 다음과 같이 객체 아이템들을 확인할 수 있습니다.

val numbers = setOf<Int>(33, 22, 11, 1, 22, 3)
println(numbers)
println("numbers.size: ${numbers.size}")
println("numbers.contains(1): ${numbers.contains(1)}")
println("numbers.isEmpty(): ${numbers.isEmpty()}")

실행 결과입니다.

[33, 22, 11, 1, 3]
numbers.size: 5
numbers.contains(1): true
numbers.isEmpty(): false

forEach 또는 Iterator 등으로 모든 객체를 탐색할 수도 있습니다.

Mutable

Mutable은 mutableSetOf<타입>(아이템들) 로 생성할 수 있습니다. Mutable이기 때문에 추가, 삭제가 가능합니다. List와 비슷한 메소드들을 지원합니다.

val numbers = mutableSetOf<Int>(33, 22, 11, 1, 22, 3)
println(numbers)
numbers.add(100)
numbers.remove(33)
println(numbers)
numbers.removeIf({ it < 10 }) // 10 이하의 숫자를 삭제
println(numbers)

실행 결과 입니다.

[33, 22, 11, 1, 3]
[22, 11, 1, 3, 100]
[22, 11, 100]

Map

Map은 key와 value를 짝지어 저장하는 Collection입니다. Map의 key는 유일하기 때문에 동일한 이름의 key는 허용되지 않습니다.에 Map 또한 Immutable과 Mutable을 별개로 지원합니다.

Immutable

Map은 mapOf<key type, value type>(아이템)로 생성할 수 있습니다. 아이템은 Pair객체로 표현하며, Pair에 key와 value를 넣을 수 있습니다. Pair(A, B)A to B로 간단히 표현이 가능합니다. 이런 문법이 가능한 것은 to가 Infix이기 때문입니다.

val numbersMap = mapOf<String, String>(
    "1" to "one", "2" to "two", "3" to "three")
println("numbersMap: $numbersMap")
val numbersMap2 = mapOf(Pair("1", "one"), Pair("2", "two"), Pair("3", "three"))
println("numbersMap2: $numbersMap2")

// 실행해보면 모두 동일한 값을 갖고 있습니다.
// numbersMap: {1=one, 2=two, 3=three}
// numbersMap2: {1=one, 2=two, 3=three}

Map의 데이터를 읽는 것도 다른 Collection과 유사합니다. getter는 get(index)[index]를 모두 지원합니다. 코틀린은 배열 방식을 선호합니다. keysvalues는 key와 value만으로 구성된 Set을 리턴해줍니다.

val numbersMap = mapOf<String, String>(
    "1" to "one", "2" to "two", "3" to "three")
println("numbersMap.get(\"1\"): ${numbersMap.get("1")}")
println("numbersMap[\"1\"]: ${numbersMap["1"]}")
println("numbersMap[\"1\"]: ${numbersMap.values}")
println("numbersMap keys:${numbersMap.keys}")
println("numbersMap values:${numbersMap.values}")

for (value in numbersMap.values) {
    println(value)
}

실행 결과입니다.

numbersMap.get("1"): one
numbersMap["1"]: one
numbersMap["1"]: [one, two, three]
numbersMap keys:[1, 2, 3]
numbersMap values:[one, two, three]
one
two
three

Mutable

Mutable은 mutableMapOf<key type, value type>(아이템)로 생성합니다. 객체 추가는 put 메소드이며, Pair를 사용하지 말고 인자로 key와 value를 넣어주면 됩니다. put도 배열 방식을 지원합니다. 그 외에 자바의 Map과 유사합니다.

val numbersMap = mutableMapOf<String, String>(
        "1" to "one", "2" to "two", "3" to "three")
println("numbersMap: $numbersMap")

numbersMap.put("4", "four")
numbersMap["5"] = "five"
println("numbersMap: $numbersMap")

numbersMap.remove("1")
println("numbersMap: $numbersMap")

numbersMap.clear()
println("numbersMap: $numbersMap")

실행 결과 입니다.

numbersMap: {1=one, 2=two, 3=three}
numbersMap: {1=one, 2=two, 3=three, 4=four, 5=five}
numbersMap: {2=two, 3=three, 4=four, 5=five}
numbersMap: {}

Collection

List와 Set은 Collection을 상속합니다. 이 클래스들은 Collection에 대입할 수 있기 때문에 아래와 같이 사용할 수 있습니다.

fun printAll(strings: Collection<String>) {
    for(s in strings) print("$s ")
    println()
}

val stringList = listOf("one", "two", "one")
printAll(stringList)

val stringSet = setOf("one", "two", "three")
printAll(stringSet)

실행 결과 입니다.

one two one
one two three

정리

코틀린의 Collection인 Map, Set, List에 대해서 알아보았습니다. 자바와 다른 것은 Mutable과 Immutable을 구분하여 지원한다는 점입니다. 그 외의 지원하는 메소드는 자바와 거의 유사합니다.

참고