[Kotlin] STL 정리하기 - Array, List, MutableList


개요

지난 번에 이어 Kotlin STL 정리하기 2편인 Array & List에 대한 포스트 입니다.


Array vs List vs MutableList

Kotlin의 배열 및 리스트 형태의 STL에 대해 얘기하기 전에 앞서, 먼저 자료구조 종류 자체를 살펴볼 필요가 있습니다. 다음과 같이 크게 3가지로 구분할 수 있고, 특징은 아래와 같습니다.

Array

  • Java의 배열로 컴파일되어 Primitive Type이다.
  • 길이가 고정되어 있다.
  • 요소들이 메모리 상에 연속적으로 할당되어 있다.
  • 인덱스를 이용해 랜덤 요소에 O(1)로 접근할 수 있다.

List

  • 길이가 고정되어 있고, immutable이다.
  • 내부적으로 연결리스트이며, 요소들이 메모리 상에 불연속적으로 할당되어 있다.
  • 인덱스를 이용해 요소에 접근할 수 있지만 O(N)가 소요된다.

MutableList (ArrayList)

  • List 타입을 상속한다.
  • Java의 ArrayList, C++의 vector와 같이 동작한다.
  • 요소들이 메모리 상에 연속적으로 할당되어 있다.
  • 인덱스를 이용해 랜덤 요소에 O(1)로 접근할 수 있다.
  • 요소를 O(1)로 맨 뒤에 추가할 수 있다
  • 용량을 동적으로 변경할 수 있고, 요소가 추가됨에 따라 필요 시 자동으로 1.5배 size-up 한다.
  • 요소 삭제 및 검색 시 O(N)가 소요된다.

LinkedList

  • 요소들이 메모리 상에 불연속적으로 할당되어 있다.
  • 특정 요소를 탐색하는 데 O(N)가 소요된다.
  • 양 끝에 대한 요소 추가 및 삭제 시 O(1)이 소요된다.

이를 살펴보면 immutable 특성을 띄는 선형 자료구조 중 ListArray에 비해 별 이점을 갖지 못하는 것을 알 수 있습니다. 심지어 비록 Array가 primitive type 이지만, Kotlin 상에서 List와 거의 동일한 STL을 지원하고 있습니다. 따라서 대부분의 경우에서 immutable 선형 자료구조가 필요하면 Array, mutable 선형 자료구조가 필요하면 MutableList를 사용하는 것이 일반적입니다. 그리고 각각 toList(), toArray() 와 같이 간편한 형변환 메서드를 제공하기 때문에 유연한 코드 작성이 가능합니다.


Array & List

immutable 선형 자료구조인 ArrayList의 경우 지원하는 STL이 거의 같기 때문에 List를 기준으로 통합하여 작성하였습니다.


size

리스트의 길이를 반환합니다.

1
abstract val size: Int
1
print(listOf(1,2,3).size) // 3


contains

특정 요소가 포함되어 있는지 체크합니다.

1
abstract fun contains(element: E): Boolean
1
print(listOf(1,2,3).contains(1)) // true


containsAll

특정 리스트에 있는 모든 요소들이 해당 리스트에 포함되어 있는지 체크합니다.

1
abstract fun containsAll(elements: Collection<E>): Boolean
1
print(listOf(1,2,3).contains(listOf(1,2))) // true


get

특정 인덱스의 요소를 반환합니다.

1
abstract operator fun get(index: Int): E
1
print(listOf(1,2,3)[0]) // 1


indexOf

특정 요소의 인덱스를 반환합니다.

1
abstract fun indexOf(element: E): Int
1
print(listOf(1,2,3).indexOf(1)) // 0


isEmpty & isNotEmpty

리스트가 비어있는지 체크합니다.

1
2
abstract fun isEmpty(): Boolean
fun <T> Collection<T>.isNotEmpty(): Boolean
1
2
print(listOf(0).isEmpty()) // false
print(listOf(0).isNotEmpty()) // true


subList

리스트의 부분 리스트를 반환합니다.

1
abstract fun subList(fromIndex: Int, toIndex: Int): List<E>
1
print(listOf(1,2,3,4).subList(1,3)) // [2, 3]


indices

리스트의 인덱스 범위를 반환합니다.

1
val Collection<*>.indices: IntRange
1
print(listOf(1,2,3).indices) // 0..2


lastIndex

리스트의 마지막 인덱스를 반환합니다.

1
val <T> List<T>.lastIndex: Int
1
print(listOf(1,2,3).lastIndex) // 2


all

리스트의 모든 요소들이 특정 조건을 만족하는지 체크합니다.

1
fun <T> Iterable<T>.all(predicate: (T) -> Boolean): Boolean
1
print(listOf(1,2,3).all { it < 4 }) // true


any

리스트의 요소들 중 하나라도 특정 조건을 만족하는지 반환합니다.

1
fun <T> Iterable<T>.any(predicate: (T) -> Boolean): Boolean
1
print(listOf(1,2,3).any { it > 3 }) // false


associate

리스트의 요소들로 key, value를 지정해 map 형태로 반환합니다.

1
2
3
fun <T, K, V> Iterable<T>.associate(
    transform: (T) -> Pair<K, V>
): Map<K, V>
1
2
3
4
data class Person(val id: Int, val name: String)
val people = listOf(Person(0, "A"), Person(1, "B"), Person(2, "C"))
val map = people.associate { it.id to it.name }
print(map) // {0=A, 1=B, 2=C}


associateBy

associate와 동일하되, key를 지정하고 value는 요소값으로 고정합니다.

1
2
3
fun <T, K> Iterable<T>.associateBy(
    keySelector: (T) -> K
): Map<K, T>
1
2
3
4
data class Person(val id: Int, val name: String)
val people = listOf(Person(0, "A"), Person(1, "B"))
val map = people.associateBy { it.id }
print(map) // {0=Person(id=0, name=A), 1=Person(id=1, name=B)}


associateWith

associate와 동일하되, value를 지정하고 key는 요소값으로 고정합니다.

1
2
3
fun <K, V> Iterable<K>.associateWith(
    valueSelector: (K) -> V
): Map<K, V>
1
2
3
4
data class Person(val id: Int, val name: String)
val people = listOf(Person(0, "A"), Person(1, "B"))
val map = people.associateWith { it.id }
print(map) // {Person(id=0, name=A)=0, Person(id=1, name=B)=1}


binarySearch

리스트에서 이진 탐색으로 특정 요소의 인덱스를 탐색하여 반환합니다.

1
2
3
4
5
fun <T : Comparable<T>> List<T?>.binarySearch(
    element: T?,
    fromIndex: Int = 0,
    toIndex: Int = size
): Int
1
print(listOf(1,2,3).binarySearch(2)) // 1


binarySearchBy

binarySearch와 동일하되, 키 값을 별도로 지정합니다.

1
2
3
4
5
6
fun <T, K : Comparable<K>> List<T>.binarySearchBy(
    key: K?,
    fromIndex: Int = 0,
    toIndex: Int = size,
    selector: (T) -> K?
): Int
1
2
3
data class Person(val id: Int, val name: String)
val people = listOf(Person(0, "A"), Person(1, "B"), Person(2, "C"))
print(people.binarySearchBy(1) { it.id }) // 1


chunked

리스트를 특정 갯수만큼의 요소로 쪼갠 리스트들의 리스트로 반환합니다.

1
fun <T> Iterable<T>.chunked(size: Int): List<List<T>>
1
print(listOf(1,2,3,4,5).chunked(2)) // [[1, 2], [3, 4], [5]]


count

리스트에서 특정 조건을 만족하는 요소들의 갯수를 반환합니다.

1
fun <T> Iterable<T>.count(predicate: (T) -> Boolean): Int
1
print(listOf(1,2,3).count { it < 3 }) // 2


distinct

리스트에서 중복 요소를 제거한 새로운 리스트를 반환합니다.

1
fun <T> Iterable<T>.distinct(): List<T>
1
print(listOf(1,3,2,1).distinct()) // [1, 2, 3]


distinctBy

distinct와 동일하되, 키 값을 지정합니다.

1
2
3
fun <T, K> Iterable<T>.distinctBy(
    selector: (T) -> K
): List<T>
1
2
3
data class Person(val id: Int, val name: String)
val people = listOf(Person(0, "A"), Person(1, "A"), Person(2, "B"))
print(people.distinctBy { it.name }) // [Person(id=0, name=A), Person(id=2, name=B)]


drop

리스트에서 첫 n개의 요소를 제거한 새로운 리스트를 반환합니다.

1
fun <T> Iterable<T>.drop(n: Int): List<T>
1
print(listOf(1,2,3).drop(1)) // [2, 3]


dropWhile

리스트의 앞에서부터 특정 조건을 만족하지 않는 요소가 등장할 때 까지 요소를 제거한 리스트를 반환합니다.

1
2
3
fun <T> Iterable<T>.dropWhile(
    predicate: (T) -> Boolean
): List<T>
1
print(listOf(1,2,3).dropWhile { it < 3 }) // [3]


dropLast

drop과 동일하나, 뒤에서부터 진행합니다.

1
fun <T> List<T>.dropLast(n: Int): List<T>
1
print(listOf(1,2,3).dropLast(1)) // [1, 2]


dropLastWhile

dropWhile과 동일하나, 뒤에서부터 진행합니다.

1
2
3
fun <T> List<T>.dropLastWhile(
    predicate: (T) -> Boolean
): List<T>
1
print(listOf(1,2,3).dropLastWhile { it > 1 }) // [1]


filter

리스트에서 특정 조건을 만족하는 요소들로만 이루어진 새로운 리스트를 반환합니다.

1
2
3
fun <T> Iterable<T>.filter(
    predicate: (T) -> Boolean
): List<T>
1
print(listOf(1,2,3,4).filter { it % 2 == 0 }) // [2, 4]


filterIndexed

filter와 동일하나, 인자로 index를 포함합니다.

1
2
3
fun <T> Iterable<T>.filterIndexed(
    predicate: (index: Int, T) -> Boolean
): List<T>
1
print(listOf(0,3,2,4).filterIndexed { idx, i -> idx == i }) // [0, 2]


filterNot

filter와 동일하나, 조건을 반전합니다.

1
2
3
fun <T> Iterable<T>.filterNot(
    predicate: (T) -> Boolean
): List<T>
1
print(listOf(1,2,3,4).filterNot { it % 2 == 0 }) // [1, 3]


find

리스트에서 특정 조건을 만족하는 첫번째 요소를 반환합니다.

1
fun <T> Iterable<T>.find(predicate: (T) -> Boolean): T?
1
print(listOf(1,2,3,4).find { it % 2 == 0 }) // 2


findLast

리스트에서 특정 조건을 만족하는 마지막 요소를 반환합니다.

1
fun <T> Iterable<T>.findLast(predicate: (T) -> Boolean): T?
1
print(listOf(1,2,3,4).findLast { it % 2 == 0 }) // 4


first

리스트의 첫번째 요소롤 반환하거나, find와 동일하게 동작합니다.

1
2
fun <T> List<T>.first(): T
fun <T> Iterable<T>.first(predicate: (T) -> Boolean): T
1
print(listOf(1,2,3).first()) // 1


flatten

중첩 리스트를 단일 리스트로 펼친 새로운 리스트를 반환합니다.

1
fun <T> Iterable<Iterable<T>>.flatten(): List<T>
1
print(listOf(listOf(1,2), listOf(3,4)).flatten()) // [1, 2, 3, 4]


flatMap

flatten와 동일하되, 요소를 변경합니다.

1
2
3
fun <T, R> Iterable<T>.flatMap(
    transform: (T) -> Iterable<R>
): List<R>
1
print(listOf(listOf(1,2), listOf(3,4)).flatMap { it.take(1) }) // [1, 3]


flatMapIndexed

flatMap과 동일하되, 인자로 index를 포함합니다.

1
2
3
fun <T, R> Iterable<T>.flatMapIndexed(
    transform: (index: Int, T) -> Iterable<R>
): List<R>
1
print(listOf(listOf(1,2), listOf(3,4)).flatMapIndexed { idx, list -> list.take(idx) }) // [3]


fold

초기값을 설정하여 리스트 요소들에 특정 연산을 순차적으로 누적한 값을 반환합니다.

1
2
3
4
fun <T, R> Iterable<T>.fold(
    initial: R,
    operation: (acc: R, T) -> R
): R
1
print(listOf(1,2,3).fold(0) { acc, i -> acc + i }) // 6


foldRight

fold와 동일하되, 역순으로 누적합니다.

1
2
3
4
fun <T, R> List<T>.foldRight(
    initial: R,
    operation: (T, acc: R) -> R
): R
1
print(listOf(1,2,3).foldRight(0) { acc, i -> acc + i }) // 6


foldIndexed

fold와 동일하되, 인자로 index를 포함합니다.

1
2
3
4
fun <T, R> Iterable<T>.foldIndexed(
    initial: R,
    operation: (index: Int, acc: R, T) -> R
): R
1
print(listOf(1,2,3).foldIndexed(0) { idx, acc, i -> acc + i * idx }) // 8


foldRightIndexed

foldIndexed와 동일하되, 역순으로 누적합니다.

1
2
3
4
fun <T, R> List<T>.foldRightIndexed(
    initial: R,
    operation: (index: Int, T, acc: R) -> R
): R
1
print(listOf(1,2,3).foldRightIndexed(0) { idx, acc, i -> acc + i * idx }) // 8


forEach

리스트의 각 요소에 대해 반복문을 실행합니다.

1
fun <T> Iterable<T>.forEach(action: (T) -> Unit)
1
listOf(1,2,3).forEach { print(it) } // 123


forEachIndexed

forEach와 동일하되, 인자로 index를 포함합니다.

1
2
fun <T> Iterable<T>.forEachIndexed(
    action: (index: Int, T) -> Unit)
1
listOf(1,2,3).forEachIndexed { idx, i -> print("$idx:$i ")} // 0:1 1:2 2:3


groupBy

리스트의 요소들을 특정 key로 grouping 한 map을 반환합니다.

1
2
3
fun <T, K> Iterable<T>.groupBy(
    keySelector: (T) -> K
): Map<K, List<T>>
1
print(listOf(1,2,3,4,5).groupBy { it % 3 }) // {1=[1, 4], 2=[2, 5], 0=[3]}


ifEmpty

리스트가 비어있을 때, 기본값을 반환합니다.

1
2
3
fun <C, R> C.ifEmpty(
    defaultValue: () -> R
): R where C : Array<*>, C : R
1
print(listOf<Int>().ifEmpty { listOf(0) }) // [0]


joinToString

리스트의 요소들을 문자열로 변환하여 이어붙인 문자열을 반환합니다.

1
2
3
4
5
6
7
8
fun <T> Iterable<T>.joinToString(
    separator: CharSequence = ", ",
    prefix: CharSequence = "",
    postfix: CharSequence = "",
    limit: Int = -1,
    truncated: CharSequence = "...",
    transform: ((T) -> CharSequence)? = null
): String
1
print(listOf(1,2,3).joinToString(" ")) // 1 2 3


last

리스트의 마지막 요소를 반환하거나, findLast와 동일하게 동작합니다.

1
2
fun <T> List<T>.last(): T
fun <T> List<T>.last(predicate: (T) -> Boolean): T
1
2
print(listOf(1,2,3).last()) // 3
print(listOf(1,2,3).last { it % 2 == 0 }) // 2


map

리스트의 각 요소들에 특정 변환을 수행한 새로운 리스트를 반환합니다.

1
fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R>
1
print(listOf(1,2,3).map { it * 10 }) // [10, 20, 30]


mapIndexed

map과 동일하나, 인자로 index를 포함합니다.

1
2
3
fun <T, R> Iterable<T>.mapIndexed(
    transform: (index: Int, T) -> R
): List<R>
1
print(listOf(1,2,3).mapIndexed { idx, i -> idx * 10 }) // [0, 20, 60]


maxOf

리스트의 요소 중 지정한 키 값이 최대인 키 값을 반환합니다.

1
2
3
fun <T, R : Comparable<R>> Iterable<T>.maxOf(
    selector: (T) -> R
): R
1
2
3
data class Person(val id: Int, val name: String)
val people = listOf(Person(0, "A"), Person(1, "A"), Person(2, "B"))
print(people.maxOf { it.id }) // 2


minOf

리스트의 요소 중 지정한 키 값이 최소인 키 값을 반환합니다.

1
2
3
fun <T, R : Comparable<R>> Iterable<T>.minOf(
    selector: (T) -> R
): R
1
2
3
data class Person(val id: Int, val name: String)
val people = listOf(Person(0, "A"), Person(1, "A"), Person(2, "B"))
print(people.minOf { it.id }) // 0


minus

리스트의 요소 중 특정 요소 또는 특성 리스트에 포함된 요소와 일치하는 요소를 제외한 새로운 리스트를 반환합니다.

1
2
3
4
operator fun <T> Iterable<T>.minus(element: T): List<T>
operator fun <T> Iterable<T>.minus(
    elements: Iterable<T>
): List<T>
1
2
println(listOf(1,2,3) - 1) // [2, 3]
println(listOf(1,2,3) - listOf(1, 2)) // [3]


minus

리스트에 특정 요소 및 리스트를 이어붙인 새로운 리스트를 반환합니다.

1
2
3
4
operator fun <T> Iterable<T>.plus(element: T): List<T>
operator fun <T> Iterable<T>.plus(
    elements: Iterable<T>
): List<T>
1
2
println(listOf(1,2,3) + 4) // [1, 2, 3, 4]
println(listOf(1,2,3) + listOf(4, 5)) // [1, 2, 3, 4, 5]


partition

리스트의 요소들을 특정 기준으로 분류한 2개의 리스트 Pair로 반환합니다.

1
2
3
fun <T> Iterable<T>.partition(
    predicate: (T) -> Boolean
): Pair<List<T>, List<T>>
1
print(listOf(1,2,3,4).partition { it % 2 == 0 }) // ([2, 4], [1, 3])


reversed

리스트의 순서를 반전한 새로운 리스트를 반환합니다.

1
fun <T> Iterable<T>.reversed(): List<T>
1
print(listOf(1,2,3).reversed()) // [3, 2, 1]


slice

리스트를 특정 범위만큼 자른 리스트를 반환합니다.

1
fun <T> List<T>.slice(indices: IntRange): List<T>
1
print(listOf(1,2,3,4).slice(1..2)) // [2, 3]


sorted

리스트를 정렬한 새로운 리스트를 반환합니다.

1
fun <T : Comparable<T>> Iterable<T>.sorted(): List<T>
1
print(listOf(1,3,2).sorted()) // [1, 2, 3]


sortedDescending

리스트를 역순으로 정렬한 새로운 리스트를 반환합니다.

1
fun <T : Comparable<T>> Iterable<T>.sortedDescending(): List<T>
1
print(listOf(1,3,2).sortedDescending()) // [3, 2, 1]


sortedBy

리스트를 특정 값을 기준으로 정렬한 새로운 리스트를 반환합니다.

1
2
3
fun <T, R : Comparable<R>> Iterable<T>.sortedBy(
    selector: (T) -> R?
): List<T>
1
2
3
4
data class Person(val id: Int, val name: String)
val people = listOf(Person(0, "A"), Person(2, "C"), Person(1, "B"))
print(people.sortedBy { it.id })
// [Person(id=0, name=A), Person(id=1, name=B), Person(id=2, name=C)]


sortedBy

리스트를 특정 값을 기준으로 역순 정렬한 새로운 리스트를 반환합니다.

1
2
3
fun <T, R : Comparable<R>> Iterable<T>.sortedByDescending(
    selector: (T) -> R?
): List<T>
1
2
3
4
data class Person(val id: Int, val name: String)
val people = listOf(Person(0, "A"), Person(2, "C"), Person(1, "B"))
print(people.sortedByDescending { it.id })
// [Person(id=2, name=C), Person(id=1, name=B), Person(id=0, name=A)]


sortedWith

Comparator를 이용해 정렬합니다. 주로 복합적인 정렬 조건일 때 사용합니다.

1
2
3
fun <T> Iterable<T>.sortedWith(
    comparator: Comparator<in T>
): List<T>
1
2
3
val list = listOf("abce", "abcd", "cdx")
// 1번째 인덱스 문자 -> 문자열 길이 기준으로 오름차순 정렬
print(list.sortedWith(Comparator({ it[1] }, { it.length })))


sumOf

리스트 요소의 특정 값의 합을 반환합니다.

1
fun <T> Iterable<T>.sumOf(selector: (T) -> Int): Int
1
2
3
data class Person(val id: Int, val name: String)
val people = listOf(Person(0, "A"), Person(2, "C"), Person(1, "B"))
print(people.sumOf { it.id }) // 3


take

리스트 앞에서부터 n개의 요소를 포함하는 새로운 리스트를 반환합니다.

1
fun <T> Iterable<T>.take(n: Int): List<T>
1
print(listOf(1,2,3).take(2)) // [1, 2]


takeWhile

리스트 앞에서부터 특정 조건을 만족하지 않는 요소가 등장하기 전까지의 요소를 갖는 새로운 리스트를 반환합니다.

1
2
3
fun <T> Iterable<T>.takeWhile(
    predicate: (T) -> Boolean
): List<T>
1
print(listOf(1,2,3).takeWhile { it < 3 }) // [1, 2]


takeLast

take와 동일하되, 뒤에서부터 포함합니다.

1
fun <T> List<T>.takeLast(n: Int): List<T>
1
print(listOf(1,2,3).takeLast(2)) // [2, 3]


takeLastWhile

takeWhile과 동일하되, 뒤에서부터 포함합니다.

1
2
3
fun <T> List<T>.takeLastWhile(
    predicate: (T) -> Boolean
): List<T>
1
print(listOf(1,2,3).takeLastWhile { it > 1 }) // [2, 3]


union

다른 리스트와 병합하여 중복 요소를 제거한 set을 반환합니다.

1
infix fun <T> Iterable<T>.union(other: Iterable<T>): Set<T>
1
print(listOf(1,2).union(listOf(2,3))) // [1, 2, 3]


zip

두 리스트를 각 인덱스의 요소 별로 합쳐 하나의 리스트로 반환합니다. 반환되는 리스트의 길이는 두 리스트의 길이 중 더 작은 값을 따릅니다.

1
2
3
4
fun <T, R, V> Iterable<T>.zip(
    other: Array<out R>,
    transform: (a: T, b: R) -> V
): List<V>
1
2
3
val list1 = listOf(1,2,3)
val list2 = listOf(4,5,6)
print(list1.zip(list2) { a, b -> a + b }) // [5, 7, 9]

두 리스트의 각 인덱스 별 요소를 Pair로 갖는 하나의 리스트를 반환합니다.

1
2
3
infix fun <T, R> Iterable<T>.zip(
    other: Iterable<R>
): List<Pair<T, R>>
1
2
3
val list1 = listOf(1,2,3)
val list2 = listOf('A', 'B' ,'C')
print(list1 zip list2) // [(1, A), (2, B), (3, C)]


unzip

Pair를 요소로 갖는 하나의 리스트를 각각 Pair의 first와 second 값을 요소로 하는 두 개의 리스트로 이루어진 Pair를 반환합니다.

1
fun <T, R> Iterable<Pair<T, R>>.unzip(): Pair<List<T>, List<R>>
1
2
3
val pair = listOf(Pair(1, 'A'), Pair(2, 'B'), Pair(3, 'C')).unzip()
println(pair.first) // [1, 2, 3]
println(pair.second) // ['A', 'B', 'C']


zipWithNext

리스트에서 인접한 모든 쌍을 Pair로 갖는 요소들로 이루어진 리스트를 반환합니다.

1
fun <T> Iterable<T>.zipWithNext(): List<Pair<T, T>>
1
print(listOf(1,2,3,4).zipWithNext()) // [(1,2), (2,3), (3,4)]


MutableList

MutableList의 경우에는 List를 상속하기 때문에 기본적으로 위에서 언급한 STL을 모두 사용할 수 있습니다. 그 외에 내부 요소를 직접 변경할 수 있는 아래와 같은 STL들을 추가적으로 지원합니다.


add

리스트의 끝 또는 특정 인덱스에 요소를 추가합니다.

1
2
abstract fun add(element: E): Boolean
abstract fun add(index: Int, element: E)
1
2
3
val list = mutableListOf(1,3)
list.add(1, 2) // [1, 2, 3]
list.add(4) // [1, 2, 3, 4]


addAll

리스트의 끝 또는 특정 인덱스에 요소들을 추가합니다.

1
2
3
4
5
6
abstract fun addAll(elements: Collection<E>): Boolean

abstract fun addAll(
    index: Int,
    elements: Collection<E>
): Boolean
1
2
3
val list = mutableListOf(1, 4)
list.addAll(1, listOf(2, 3)) // [1, 2, 3, 4]
list.addAll(listOf(5, 6)) // [1, 2, 3, 4, 5, 6]


clear

리스트를 비웁니다.

1
abstract fun clear()
1
2
val list = mutableListOf(1,2,3)
list.clear() // []


remove

리스트에서 특정 요소를 앞에서 하나 제거합니다.

1
abstract fun remove(element: E): Boolean
1
2
val list = mutableListOf(1,1,1)
list.remove(1) // [1, 1]


removeAll

리스트에서 특정 조건을 만족하는 요소들을 모두 제거합니다.

1
2
3
fun <T> MutableList<T>.removeAll(
    predicate: (T) -> Boolean
): Boolean
1
2
val mutableListOf(1,2,3,4)
list.removeAll { it % 2 == 0 } // [1, 3]


removeAt

리스트에서 특정 인덱스의 요소를 제거합니다.

1
abstract fun removeAt(index: Int): E
1
2
val list = mutableListOf(1,2,3)
list.removeAt(1) // [1, 3]


removeFirst

리스트의 가장 앞 요소를 제거합니다. 존재하지 않을 시 예외를 발생시킵니다.

1
fun <T> MutableList<T>.removeFirst(): T
1
2
val list = mutableListOf(1,2,3)
list.removeFirst() // [2, 3]


removeLast

리스트의 가장 뒤 요소를 제거합니다. 존재하지 않을 시 예외를 발생시킵니다.

1
fun <T> MutableList<T>.removeLast(): T
1
2
val list = mutableListOf(1,2,3)
list.removeLast() // [1, 2]


retainAll

리스트에서 특정 조건을 만족하는 요소들만 남겨둡니다.

1
2
3
fun <T> MutableList<T>.retainAll(
    predicate: (T) -> Boolean
): Boolean
1
2
val list = mutableListOf(1,2,3,4)
list.retainAll { it % 2 == 0} // [2, 4]


set

리스트에서 특정 인덱스의 요소를 변경합니다.

1
abstract operator fun set(index: Int, element: E): E
1
2
val list = mutableListOf(1,0,3)
list.set(1,2) // [1, 2, 3]


plusAssign

리스트에 요소를 연산자로 추가합니다.

1
2
operator fun <T> MutableCollection<in T>.plusAssign(element: T)
operator fun <T> MutableCollection<in T>.plusAssign(elements: Iterable<T>)
1
2
3
val list = mutableListOf(1,2,3)
list += 4 // [1, 2, 3, 4]
list += listOf(5,6) // [1, 2, 3, 4, 5, 6]


minusAssign

리스트에 요소를 연산자로 제거합니다.

1
2
operator fun <T> MutableCollection<in T>.minusAssign(element: T)
operator fun <T> MutableCollection<in T>.minusAssign(elements: Iterable<T>)
1
2
3
val list = mutableListOf(1,2,3,4,5,6)
list -= 6 // [1, 2, 3, 4, 5]
list -= listOf(4,5,6) // [1, 2, 3]


그 외에 reversed()sorted() 같이 수동태로 쓰여진 메서드와 같은 경우, 능동태로 변환한 reverse()sort()MutableList에서 사용 가능합니다. 능동태 메서드의 경우에는 변환 시킨 리스트를 반환할 뿐만 아니라, mutable 답게 원본 리스트까지 실제로 수정한다는 차이점이 있습니다.


Numbers

그리고 IntDouble과 같이 Numbers 타입을 요소로 갖는 리스트의 경우, 다음과 같은 확장 함수를 이용할 수 있습니다.

  • List<T>.max(): 리스트의 최댓값 반환
  • List<T>.min(): 리스트의 최솟값 반환
  • List<T>.sum(): 리스트의 누적합 반환
  • List<T>.average(): 리스트의 평균값 반환


응용

리스트에서 인접한 중복 요소 제거

1
List<T>.zipWithNext().filter { it.first != it.second }.map { it.first } + List<T>.last()
1
2
3
val list = listOf(1, 1, 2, 3, 4, 4, 1, 2, 2, 3, 3, 4)
print(list.zipWithNext().filter{ it.first != it.second }.map{ it.first } + list.last())
// [1, 2, 3, 4, 1, 2, 3, 4]


마치며

Kotlin은 리스트에 대한 STL이 굉장히 풍부한 편이기 때문에, 종종 알고리즘 문제 해결 사이트에서 복잡한 문제에 대해 한 줄 짜리 답안을 심심치 않게 볼 수 있습니다. 비록 PS에서 답안 코드의 길이가 중요하지는 않습니다만, 문제 풀이 속도에는 상당히 큰 영향을 미치므로 리스트에 대한 STL 학습은 굉장히 중요한 요소라고 생각합니다.

0%