Collection
코틀린은 기본적으로 array, list, Set, Map과 튜플 형태의 Pair, Triple을 제공합니다.
추가로 일반적인 컬렉션에는 immutable, mutable 두가지 형태를 모두 제공합니다.
- pair : 데이터가 2개 저장되는 튜플
- triple : 데이터가 3개 저장되는 튜플
- array : 크키가 고정된 동일한 타입의 변수의 묶음
- list : array에서 크기를 조절할 수 있는 자료 형태(순서가 있음)
- set : list에서 순서가 없는 형태
- map : 키에 매핑된 데이터를 저장하는 데이터 구조
collection 활용법
생성
1 | val l = listOf(1,2,3,4,5) |
기본적인 list, map, set 은 immutable 하기 때문에 생성 후 수정이 불가능합니다.
내부의 인터페이스 자체가 분리되어 있는데(정확히는 Mutable 버전이 immutable 버전을 상속한 형태), 자바처럼 런타임에서 확인하는 것이 아닌 컴파일 타임에서 바로 확인이 가능하다는 장점이 있습니다.
변경이 필요한 경우엔 mutable 버전으로 사용해야 합니다.
1 | val l = mutableListOf(1,2,3,4,5) |
내부 데이터 순회
1 | val l = listOf(1,2,3,4,5) |
데이터의 순회는 for문으로 가능합니다. 이는 내부에 iterator를 구현하고
있어야 합니다.
또한 foreach 과정에서 index를 가져오게 할 수도 있습니다.
1 | val s = listOf(1,2,3) |
withIndex는 각 엘레먼트의 값과 인덱스값을 가지는 구조체인 IndexedValue 의 iterator 를 리턴합니다.
내부 데이터 확인
in 연산자를 이용해 내부에 데이터가 있는지 확인 할 수 있습니다.
1 | val l = listOf(1,2,3,4,5) |
Pair, Triple
2개, 3개의 값을 저장하는 구조체입니다.
데이터는 immutable 하여 내부 데이터를 수정할 수는 없습니다.
만약 4개이상의 데이터를 저장하고 싶다면 차라리 별도의 data class
를 정의하는게 나을 수도 있습니다.
pair의 경우 아래와 같이 편하게 선언 할 수도 있습니다.
1 | 1 to 2 |
to 함수는 모든 객체에 사용 할 수 있는 확장 함수로 함수 양쪽의 값을 하나의 Pair 객체로 묶을 수 있습니다. 이는 map을 만들 때도 유용합니다.
1 | val m: Map<Int, String> = mapOf(1 to "One", 2 to "Two", 3 to "Three") |
객체 배열과 프리미티브 배열
코틀린에서는 배열을 표현하기 위해 Array 라는 클래스가 존재합니다.
코틀린 자체에서는 kotlin.Array로 인식되며, jvm 에서는 단순 array 로 인식됩니다.
1 | val intArr = arrayOf(1,2,3) |
위 코드에서 intArr는 java에서 Integer[]
로 인식 됩니다. 일반적인 클래스라면 크게 문제가 될 상황은 아니지만 int, float 처럼 primitive 타입이 있는 경우에는 박싱된 객체를 사용하기 때문에 최적화에 이슈가 있을 수 있습니다. 뿐만 아니라 Array 는 모든 클래스에서 사용되는 공용 클래스이기 때문에 아래와 같이 특화된 기능을 제공하지 않습니다.
1 | val intArr = intArrayOf(1,2,3) |
배열은 생성 할 때 생성 함수를 이용해 각 인덱스에 할당될 값을 생성 할 수 있습니다.
1 | val evens = IntArray(3) {i -> i*2} |
이는 리스트에서는 지원하지 않는 메소드입니다.
다만 array 보다는 list가 기능이 더 많기 때문에 일반적인 상황에서는 list를 사용하는 것이 좋습니다.
List
리스트는 immutable, mutable 한 인터페이스 둘 다 제공하고 있습니다.
각각 listOf
, mutableListOf
라는 함수로 각각 jvm 기준 Arrays$ArrayList
, ArrayList
를 생성합니다.
재미 있는 점은 코틀린에서는 두 타입의 리스트는 인터페이스가 분리되어 있습니다. List
와 MutableList
로 MutableList
가 List
를 상속하여 mutable 한 메소드를 확장한 형태를 가집니다.
1 | var l = listOf(1,2,3) |
코틀린에서는 list 의 특정 인덱스상의 값을 불러오기 위해 get
을 사용할 필요가 없습니다. 배열에서 사용하듯 [] 연산자를 사용하면 됩니다.
또한 contains
대신 in
, set
대신 l[1] = 3
처럼 사용도 가능합니다. 이외에 +, - 연산자도 오버라이딩하여 데이터를 추가/삭제(List
에서는 새로운 객체를 생성)
Set
Set 은 순서가 없는 리스트로 생각하시면 됩니다. 또한 데이터가 중복되지 않는다는 특징이 있습니다.
기본적으로 setOf
함수를 통해 생성할 수 있으며 jvm 기준 LinkedHashSet
을 사용하고 있습니다. 다만 인터페이스는 immutable 한 set이기 때문에 해당 객체에 데이터를 추가 할 수는 없습니다.
Set
의 대부분의 인터페이스가 List
와 비슷하지만 순서가 없기 때문에, []를 통해 개별 데이터를 접근 할 수 없습니다.
1 | setOf(1,2,3).filter {it == 2}.forEach{println(it)} // 2 |
극단적인 예시이지만 원하는 데이터를 Set에서 찾아야 할 땐 위처럼 filter 연산을 통해 걸러내야합니다.
Map
맵은 두 타입의 데이터를 매핑하는 정보를 저장하는 클래스입니다.
코틀린에서는 다른 컬랙션과 마찬가지로 immutable, mutable 한 클래스를 지원합니다. 또한 객체 생성을 위해 생성 함수가 존재하며 아래와 같이 사용할 수 있습니다.
1 | val m = mapOf("a" to 1, "b" to 2) |
mapOf는 키, 밸류 순서대로 정의된 Pair
객체들의 배열로 구성합니다.
list 와 마찬가지로 in
연산자를 지원합니다. 다만 이 key 에 대해서만 확인이 가능합니다. 값에 대한 포함여부를 확인하고 싶은 경우 containsValue
메소드를 이용하여 확인해야 합니다.
[] 연산자 역시 특이한데, 용법은 List
와 동일하지만 get의 결과 타입이 다릅니다.
1 | val one:Int? = m["a"] |
일반 List 였다면 리턴타입이 Int 였겠지만 map의 경우 없는키로 요청하는 경우도 처리해야하기 때문에 (List에서는 IndexOutOfBoundsException가 발생합니다.) Nullable 한 타입(기존 타입에 ?가 추가된 형태)을 리턴합니다. 따라서 이에 대해 추가적인 확인 절차가 필요합니다.
순회의 경우 내부에 iterator를 구현했기 때문에 foreach 문에서도 활용이 가능합니다.
1 | for ((k, v) in m) { |
iterator는 Iterator<Entry<K,V>>
객체를 리턴하고 foreach 는 각각의 Entry
객체를 가지고 반복하게 되는데, 이때 Entry
는 component 메소드가 구현되어 있기 때문에 코틀린의 객체 destruction 에 의해 분리되어 각 개별 변수에 할당되어 사용 할 수 있습니다.
출처
- 다재다능 코틀린 프로그래밍
- 공식 문서