List란 무엇일까요.
배열(Array)는 연속된 데이터의 집합으로, 메모리에 물리적으로 연속되어 저장됩니다.
하지만 그렇다보니, 단점이 하나 있습니다. 바로 크기가 고정되어 있다는 것이죠.
한 번 생성하면, 크기를 줄이거나 늘리지 못합니다.
이러한 점 때문에 리스트(List)가 등장하였습니다.
리스트는 각 원소가 다음 원소의 주솟값(포인터)를 갖고있어,
메모리에 물리적 연속이 아니라 논리적으로 서로 연속되어 있습니다.
즉, 크기를 줄였다 늘였다 할 수 있는 가변 크기의 연속된 데이터이죠.
다만, 물리적 연속이 아닌 논리적 연속이기 때문에 속도는 배열보다 느립니다.
불변(Immutable) 리스트
val list = listOf(1, 2, 3)
val list2 = List(3) { it }
코틀린의 List는 기본적으로 불변(Immutable) 입니다.
한 번 선언 및 생성하면 새로운 값을 추가 할 수도, 수정할 수도, 제거할 수도 없는
읽기 전용(read - only) 리스트입니다.
뒤에서 나올 추가/삭제/수정 가능한 가변(Mutable) 리스트는 모두 이 리스트를 기반으로 만들어졌습니다.
val arr = arrayOf(1,2,3)
val list = listOf(1,2,3)
arr[1] = 2 // val로 생성되었지만 내부 배열은 변경 가능
list[1] = 2 // 내부 배열 변경 불가
val와 var의 차이 편에서, 배열은 val로 선언되어도 재할당이 불가능하다는 것 이지,
해당 변수가 가르키는 객체 내부의 값은 변경이 가능하므로,
배열 내 원소들의 값은 변경이 가능하다는 점, 기억하시나요?
List는 리스트 내 원소의 삽입/삭제 뿐만 아니라, 수정까지 불가능하기 때문에 더 높은 불변성을 제공합니다.
가변(Mutable) 리스트
MutableList
MutableList는 이름처럼, 처음에 소개한 리스트의 측성인 가변길이를 갖고있는 리스트입니다.
val list = MutableList<Int>(5) {it} // 0, 1, 2, 3, 4
list.add(5) // 리스트 끝에 5 추가 - 0, 1, 2, 3, 4, 5
list.remove(2) // 리스트 내 '2' 원소 제거 - 0, 1, 3, 4
list.removeAt(0) // 리스트의 0번째 인덱스 원소 제거 - 1, 3, 4
list[2] = 100 // 리스트의 2번째 인덱스를 100으로 변경 => -1, 3, 100
println(list[2]) // 리스트 2번째 원소를 출력 -> 100
// mutableListOf 로도 생성 가능
val list2 = mutableListOf(1,2,3,4,5)
MutableList는 자바의 ArrayList와 크게 다르지 않습니다.
add(n)을 통해 리스트의 맨 끝에 원소 n을 삽입할 수 있고,
remove(n)을 통해 리스트 내 원소 n을 제거할 수 있습니다.
removeAt(n)을 통해 리스트 내 n번째 원소를 제거할 수 있죠.
다만, 자바의 list.get(n), list.set(3,4)와 같이 get/set 대신
코틀린에서는 list[n]으로 간단히 접근 가능합니다.
ArrayList
val arrayList = ArrayList<Int>()
arrayList.remove(2)
arrayList.removeAt(0)
list[2] = 100
println(list[2])
println(list.isEmpty()) // 리스트가 비어있는지 확인 -> 비어있지 않으므로 false 반환
println(list.isNotEmpty() // 리스트가 차있는지 확인 -> 차있으므로 true 반환
// arrayListOf로도 생성 가능
val list = arrayListOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
자바를 다뤘던 분들이라면 꽤나 익숙하실 ArrayList 입니다.
사용방법은 생성할 때 MutableList와 같이 Mutable(3) { it } 처럼 람다식을 사용한 생성이 불가능하다는 점 정도려나요.
사실... MutableList라는 녀석이 있는지 조차 저는 이 시리즈를 준비하며 알게 되었습니다.
저 역시 자바에서 코틀린으로 넘어온 개발자이다 보니,
주구장장 ArrayList/LinkedList만 써봤지, MutableList는 최근에 알게 됬습니다.
이 둘의 차이가 궁금하여 인터넷을 뒤지던 중, 스택 오버플로우의 한 글을 보게 되었습니다.
요약하자면, ArrayList는 MutableList의 종류 중 하나로써,
"ArrayList로 생성하는 것은, 나는 MutableList의 여러 종류 중 특별히 ArrayList를 사용하겠다"
라는 정도의 의미 정도일 뿐, 동작과정은 별 차이가 없다고 합니다.
사실 코틀린에서 mutableListOf로 생성하면 그냥 ArrayList가 반환된다고 합니다.
MutableList가 낮선 분들이라면 그냥 ArrayList를 써도 무방할 것 같네요.
LinkedList
val list = LinkedList<Int>()
list.add(6)
list.remove(2)
list.removeAt(1)
ArrayList와는 내부 구조만 다를 뿐 사용방법은 동일합니다.
ArrayList vs LinkedList
ArrayList는 이름에서 유추할 수 있듯, 내부에 배열(Array)를 두어 활용하는 리스트입니다.
즉, 물리적 연속이라는 배열의 특성을 가지고 있는 리스트입니다.
내부에 배열을 두고 ,원소가 가득 차면 리사이징하는 방식으로 작동되며,
때문에 삽입과 삭제가 빈번히 일어나면 효율이 떨어집니다.
다만, 배열을 응용하는 만큼, 인덱스를 갖고 있어 검색이나 수정에 유리합니다.
반면 LinkedList는 물리적보단 논리적 연결에 더 가까운 방법입니다.
각각의 원소가 다음 원소의 주소를 갖고 있는 형태지요.
구현 방식에 따라 다음 원소의 주솟값 뿐만 아니라 이전 원소의 주솟값까지 갖고 있는 경우도 있는데,
자바/코틀린 표준 라이브러리의 LinkedList는 후자의 형태입니다.
LinkedList는 배열의 크기를 리사이징 할 필요 없는 만큼, 삽입 삭제가 빈번할수록 효과적이나,
인덱스가 없어, 검색이나 수정 효율은 ArrayList 보다 떨어집니다.
ArrayList - 인덱스 사용, 내부에 배열을 둠, 검색 및 수정에 유리
LinkedList - 인덱스 없음, 논리적 연결, 삽입 및 삭제에 유리
정리하자면 위와 같으며, 상황에 따라 더 적합한 리스트를 활용하면 됩니다.
List의 메소드들
val arrayList = arrayListOf(1, 2, 5, 3, 2, 1, 2, 53645, 234, 7, 8, 9, 10)
println(arrayList.max())
println(arrayList.min())
println(arrayList.average())
println(arrayList.sum())
println(arrayList.reversed())
println(arrayList.sorted())
// 출력
>>> 53645
1
4148.384615384615
53929
[10, 9, 8, 7, 234, 53645, 2, 1, 2, 3, 5, 2, 1]
[1, 1, 2, 2, 2, 3, 5, 7, 8, 9, 10, 234, 53645]
리스트 역시 마찬가지로 max, min, average, sum, reversed, sorted 등등 메소드를 제공합니다.
관련된 기능은 매우 많으므로, 더 많은 기능을 보고 싶다면 코틀린 공식 문서를 참고해주세요.
https://kotlinlang.org/docs/home.html
부록. List는 완벽한 불변성을 제공할까?
결론부터 말하자면 아니요. 입니다.
MutableList는 List를 기반으로 만든 형태라고 했죠?
그렇다면, List를 MutableList로 형변환하는 것이 가능합니다.
fun main() {
val list = listOf(1, 2, 3, 4, 5)
val mutableList = list as MutableList // 가변리스트로 형변환
mutableList[2] = -1
println(list[2])
println(mutableList[2])
}
// 출력
-1
-1
as는 형변환 연산자로써, 자바에서 앞에 (int) 등을 붙여 명시적 형변환을 하는 것과 같은 역할을 합니다.
as를 사용해 가변 리스트로 변형한 후에,
원소의 값을 바꿨더니 원본이 되는 리스트의 값 역시 갱신되었습니다.
List 역시 완벽한 불변성을 제공하지는 않는다는 것이지요.
'코틀린 파헤치기 > 1부. 코틀린 기초' 카테고리의 다른 글
[코파기 1부] 10. 코틀린과 반복문 (2) : 흐름제어 - continue, break + 라벨링 (0) | 2023.03.15 |
---|---|
[코파기 1부] 9. 코틀린과 반복문 (1) : for, while, do-while, repeat, forEach (2) | 2023.03.15 |
[코파기 1부] 7. 코틀린과 배열 (Array) (0) | 2023.03.14 |
[코파기 1부] 6. 코틀린과 null (.? !!. ?:) (0) | 2023.03.14 |
[코파기 1부] 5. 코틀린과 조건문. 그리고 표현식 (0) | 2023.03.14 |