개요
지난 번에 이어 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 특성을 띄는 선형 자료구조 중 List가 Array에 비해 별 이점을 갖지 못하는 것을 알 수 있습니다. 심지어 비록 Array가 primitive type 이지만, Kotlin 상에서 List와 거의 동일한 STL을 지원하고 있습니다. 따라서 대부분의 경우에서 immutable 선형 자료구조가 필요하면 Array, mutable 선형 자료구조가 필요하면 MutableList를 사용하는 것이 일반적입니다. 그리고 각각 toList(), toArray() 와 같이 간편한 형변환 메서드를 제공하기 때문에 유연한 코드 작성이 가능합니다.
Array & List
immutable 선형 자료구조인 Array와 List의 경우 지원하는 STL이 거의 같기 때문에 List를 기준으로 통합하여 작성하였습니다.
size
리스트의 길이를 반환합니다.
1 | |
1 | |
contains
특정 요소가 포함되어 있는지 체크합니다.
1 | |
1 | |
containsAll
특정 리스트에 있는 모든 요소들이 해당 리스트에 포함되어 있는지 체크합니다.
1 | |
1 | |
get
특정 인덱스의 요소를 반환합니다.
1 | |
1 | |
indexOf
특정 요소의 인덱스를 반환합니다.
1 | |
1 | |
isEmpty & isNotEmpty
리스트가 비어있는지 체크합니다.
1 | |
1 | |
subList
리스트의 부분 리스트를 반환합니다.
1 | |
1 | |
indices
리스트의 인덱스 범위를 반환합니다.
1 | |
1 | |
lastIndex
리스트의 마지막 인덱스를 반환합니다.
1 | |
1 | |
all
리스트의 모든 요소들이 특정 조건을 만족하는지 체크합니다.
1 | |
1 | |
any
리스트의 요소들 중 하나라도 특정 조건을 만족하는지 반환합니다.
1 | |
1 | |
associate
리스트의 요소들로 key, value를 지정해 map 형태로 반환합니다.
1 | |
1 | |
associateBy
associate와 동일하되, key를 지정하고 value는 요소값으로 고정합니다.
1 | |
1 | |
associateWith
associate와 동일하되, value를 지정하고 key는 요소값으로 고정합니다.
1 | |
1 | |
binarySearch
리스트에서 이진 탐색으로 특정 요소의 인덱스를 탐색하여 반환합니다.
1 | |
1 | |
binarySearchBy
binarySearch와 동일하되, 키 값을 별도로 지정합니다.
1 | |
1 | |
chunked
리스트를 특정 갯수만큼의 요소로 쪼갠 리스트들의 리스트로 반환합니다.
1 | |
1 | |
count
리스트에서 특정 조건을 만족하는 요소들의 갯수를 반환합니다.
1 | |
1 | |
distinct
리스트에서 중복 요소를 제거한 새로운 리스트를 반환합니다.
1 | |
1 | |
distinctBy
distinct와 동일하되, 키 값을 지정합니다.
1 | |
1 | |
drop
리스트에서 첫 n개의 요소를 제거한 새로운 리스트를 반환합니다.
1 | |
1 | |
dropWhile
리스트의 앞에서부터 특정 조건을 만족하지 않는 요소가 등장할 때 까지 요소를 제거한 리스트를 반환합니다.
1 | |
1 | |
dropLast
drop과 동일하나, 뒤에서부터 진행합니다.
1 | |
1 | |
dropLastWhile
dropWhile과 동일하나, 뒤에서부터 진행합니다.
1 | |
1 | |
filter
리스트에서 특정 조건을 만족하는 요소들로만 이루어진 새로운 리스트를 반환합니다.
1 | |
1 | |
filterIndexed
filter와 동일하나, 인자로 index를 포함합니다.
1 | |
1 | |
filterNot
filter와 동일하나, 조건을 반전합니다.
1 | |
1 | |
find
리스트에서 특정 조건을 만족하는 첫번째 요소를 반환합니다.
1 | |
1 | |
findLast
리스트에서 특정 조건을 만족하는 마지막 요소를 반환합니다.
1 | |
1 | |
first
리스트의 첫번째 요소롤 반환하거나, find와 동일하게 동작합니다.
1 | |
1 | |
flatten
중첩 리스트를 단일 리스트로 펼친 새로운 리스트를 반환합니다.
1 | |
1 | |
flatMap
flatten와 동일하되, 요소를 변경합니다.
1 | |
1 | |
flatMapIndexed
flatMap과 동일하되, 인자로 index를 포함합니다.
1 | |
1 | |
fold
초기값을 설정하여 리스트 요소들에 특정 연산을 순차적으로 누적한 값을 반환합니다.
1 | |
1 | |
foldRight
fold와 동일하되, 역순으로 누적합니다.
1 | |
1 | |
foldIndexed
fold와 동일하되, 인자로 index를 포함합니다.
1 | |
1 | |
foldRightIndexed
foldIndexed와 동일하되, 역순으로 누적합니다.
1 | |
1 | |
forEach
리스트의 각 요소에 대해 반복문을 실행합니다.
1 | |
1 | |
forEachIndexed
forEach와 동일하되, 인자로 index를 포함합니다.
1 | |
1 | |
groupBy
리스트의 요소들을 특정 key로 grouping 한 map을 반환합니다.
1 | |
1 | |
ifEmpty
리스트가 비어있을 때, 기본값을 반환합니다.
1 | |
1 | |
joinToString
리스트의 요소들을 문자열로 변환하여 이어붙인 문자열을 반환합니다.
1 | |
1 | |
last
리스트의 마지막 요소를 반환하거나, findLast와 동일하게 동작합니다.
1 | |
1 | |
map
리스트의 각 요소들에 특정 변환을 수행한 새로운 리스트를 반환합니다.
1 | |
1 | |
mapIndexed
map과 동일하나, 인자로 index를 포함합니다.
1 | |
1 | |
maxOf
리스트의 요소 중 지정한 키 값이 최대인 키 값을 반환합니다.
1 | |
1 | |
minOf
리스트의 요소 중 지정한 키 값이 최소인 키 값을 반환합니다.
1 | |
1 | |
minus
리스트의 요소 중 특정 요소 또는 특성 리스트에 포함된 요소와 일치하는 요소를 제외한 새로운 리스트를 반환합니다.
1 | |
1 | |
minus
리스트에 특정 요소 및 리스트를 이어붙인 새로운 리스트를 반환합니다.
1 | |
1 | |
partition
리스트의 요소들을 특정 기준으로 분류한 2개의 리스트 Pair로 반환합니다.
1 | |
1 | |
reversed
리스트의 순서를 반전한 새로운 리스트를 반환합니다.
1 | |
1 | |
slice
리스트를 특정 범위만큼 자른 리스트를 반환합니다.
1 | |
1 | |
sorted
리스트를 정렬한 새로운 리스트를 반환합니다.
1 | |
1 | |
sortedDescending
리스트를 역순으로 정렬한 새로운 리스트를 반환합니다.
1 | |
1 | |
sortedBy
리스트를 특정 값을 기준으로 정렬한 새로운 리스트를 반환합니다.
1 | |
1 | |
sortedBy
리스트를 특정 값을 기준으로 역순 정렬한 새로운 리스트를 반환합니다.
1 | |
1 | |
sortedWith
Comparator를 이용해 정렬합니다. 주로 복합적인 정렬 조건일 때 사용합니다.
1 | |
1 | |
sumOf
리스트 요소의 특정 값의 합을 반환합니다.
1 | |
1 | |
take
리스트 앞에서부터 n개의 요소를 포함하는 새로운 리스트를 반환합니다.
1 | |
1 | |
takeWhile
리스트 앞에서부터 특정 조건을 만족하지 않는 요소가 등장하기 전까지의 요소를 갖는 새로운 리스트를 반환합니다.
1 | |
1 | |
takeLast
take와 동일하되, 뒤에서부터 포함합니다.
1 | |
1 | |
takeLastWhile
takeWhile과 동일하되, 뒤에서부터 포함합니다.
1 | |
1 | |
union
다른 리스트와 병합하여 중복 요소를 제거한 set을 반환합니다.
1 | |
1 | |
zip
두 리스트를 각 인덱스의 요소 별로 합쳐 하나의 리스트로 반환합니다. 반환되는 리스트의 길이는 두 리스트의 길이 중 더 작은 값을 따릅니다.
1 | |
1 | |
두 리스트의 각 인덱스 별 요소를 Pair로 갖는 하나의 리스트를 반환합니다.
1 | |
1 | |
unzip
Pair를 요소로 갖는 하나의 리스트를 각각 Pair의 first와 second 값을 요소로 하는 두 개의 리스트로 이루어진 Pair를 반환합니다.
1 | |
1 | |
zipWithNext
리스트에서 인접한 모든 쌍을 Pair로 갖는 요소들로 이루어진 리스트를 반환합니다.
1 | |
1 | |
MutableList
MutableList의 경우에는 List를 상속하기 때문에 기본적으로 위에서 언급한 STL을 모두 사용할 수 있습니다. 그 외에 내부 요소를 직접 변경할 수 있는 아래와 같은 STL들을 추가적으로 지원합니다.
add
리스트의 끝 또는 특정 인덱스에 요소를 추가합니다.
1 | |
1 | |
addAll
리스트의 끝 또는 특정 인덱스에 요소들을 추가합니다.
1 | |
1 | |
clear
리스트를 비웁니다.
1 | |
1 | |
remove
리스트에서 특정 요소를 앞에서 하나 제거합니다.
1 | |
1 | |
removeAll
리스트에서 특정 조건을 만족하는 요소들을 모두 제거합니다.
1 | |
1 | |
removeAt
리스트에서 특정 인덱스의 요소를 제거합니다.
1 | |
1 | |
removeFirst
리스트의 가장 앞 요소를 제거합니다. 존재하지 않을 시 예외를 발생시킵니다.
1 | |
1 | |
removeLast
리스트의 가장 뒤 요소를 제거합니다. 존재하지 않을 시 예외를 발생시킵니다.
1 | |
1 | |
retainAll
리스트에서 특정 조건을 만족하는 요소들만 남겨둡니다.
1 | |
1 | |
set
리스트에서 특정 인덱스의 요소를 변경합니다.
1 | |
1 | |
plusAssign
리스트에 요소를 연산자로 추가합니다.
1 | |
1 | |
minusAssign
리스트에 요소를 연산자로 제거합니다.
1 | |
1 | |
그 외에 reversed()와 sorted() 같이 수동태로 쓰여진 메서드와 같은 경우, 능동태로 변환한 reverse()와 sort()로 MutableList에서 사용 가능합니다. 능동태 메서드의 경우에는 변환 시킨 리스트를 반환할 뿐만 아니라, mutable 답게 원본 리스트까지 실제로 수정한다는 차이점이 있습니다.
Numbers
그리고 Int 및 Double과 같이 Numbers 타입을 요소로 갖는 리스트의 경우, 다음과 같은 확장 함수를 이용할 수 있습니다.
List<T>.max(): 리스트의 최댓값 반환List<T>.min(): 리스트의 최솟값 반환List<T>.sum(): 리스트의 누적합 반환List<T>.average(): 리스트의 평균값 반환
응용
리스트에서 인접한 중복 요소 제거
1 | |
1 | |
마치며
Kotlin은 리스트에 대한 STL이 굉장히 풍부한 편이기 때문에, 종종 알고리즘 문제 해결 사이트에서 복잡한 문제에 대해 한 줄 짜리 답안을 심심치 않게 볼 수 있습니다. 비록 PS에서 답안 코드의 길이가 중요하지는 않습니다만, 문제 풀이 속도에는 상당히 큰 영향을 미치므로 리스트에 대한 STL 학습은 굉장히 중요한 요소라고 생각합니다.
- Post link: https://blog.yjyoon.dev/kotlin/2023/03/14/kotlin-02/
- Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 3.0 unless stating additionally.