// 형 지정을 자동화 하려면 제너릭 지정 필요 val emptyList1: MutableList<String> = mutableListOf() val emptyList2 = mutableListOf<String>() val emptySet1: MutableSet<String> = mutableSetOf() val emptySet2 = mutableSetOf<String>()
List 콜렉션 뿐 아니라 앞으로 소개할 콜렉션에서도 immutable, mutable 콜렉션으로 나뉘며 immutable, mualbe 간 변환할 수 있는 메서드를 제공한다.
1 2 3 4 5
val list = listOf(1, 2, 3) val mlist = mutableListOf(1, 2, 3)
val list2 = mlist.toList() val mlist2 = list.toMutableList()
구조분해, 오버라이딩 함수
List 에 기본적으로 component1 ~ component5 함수가 정의되어 있는데 아래 사용처럼 구조분해 문법 사용이 가능하다.
1 2 3 4
val range: CharRange = 'a'..'g' val (a, b, c, d, e) = range.toList(); println("$a$b$c$d$e") // a b c d e // val (a1, b1, c1, d1, e1, f1) = range.toList(); 에러 발생 component6 가 없기 때문
kotlin 의 연잔사 오버라이딩을 통해 +, - 연산자로 새로운 콜렉션을 생성해서 요소를 추가하거나 삭제할 수 있다.
1 2 3 4 5 6 7
val fruits: List<String> = listOf("Apple", "Banana", "Grape")
Pair 는 값이 2개인 튜플, Triple 값이 3개인 튜플, 둘다 kotlin 의 표준 콜렉션 클레스이다.
1 2 3 4 5 6 7 8 9 10
publicdataclassPair<out A, out B>( publicval first: A, publicval second: B ) : Serializable { ... }
publicdataclassTriple<out A, out B, out C>( publicval first: A, publicval second: B, publicval third: C ) : Serializable { ... }
1 2 3
val p1: Pair<String, Int> = "a" to 1 val p2: Pair<String, Int> = Pair("b", 2) val t1: Triple<String, String, String> = Triple("John", "Quincy", "Adams")
PairTriple 모두 구조분해 연산자를 제공한다. 아래처럼 값을 추출하면 좀더 간결하게 값을 할당할 수 있다.
funmain() { val (one, two, three) = getFullName() println("$one$two$three") // John Quincy Adams val (first, _, middle) = getFullName() println("$first$middle") // John Adams val (only) = getFullName() println("$only") // John val full = getFullName() println("$full") // (John, Quincy, Adams) }
중간에 값이 빌 경우 언더바를 통해 생략할 수 있다.
Map
Map 는 immutable 콜렉션. MutableMap 는 mutable 표준 콜렉션.
Pair 객체를 사용해서 생성할 수 있다.
1 2 3 4 5 6
val p1 = "a" to 1 val p2 = Pair("b", 2) val map = mapOf(p1, p2, "c" to 3) val mmap = mutableMapOf(p1, p2, "c" to 3) println(map) // {a=1, b=2, c=3} println(mmap) // {a=1, b=2, c=3}
get, set 메서드는 인덱스 연산자로 대체해서 사용할 수 있다.
1 2 3 4 5 6
val mmap = mutableMapOf("hello" to "world", "foo" to "bar")
println(mmap.get("hello")) // world println(mmap["foo"]) // bar mmap["baz"] = "qux" println(mmap) // {hello=world, foo=bar, baz=qux}
associate, associateWith
콜렉션간의 결합을 통해 Map 콜렉션 생성이 가능하다.
1 2 3
var keys: CharRange = 'a'..'f' val map: Map<Char, String> = keys.associate { it to it.toString().repeat(5).capitalize() } println(map) // {a=Aaaaa, b=Bbbbb, c=Ccccc, d=Ddddd, e=Eeeee, f=Fffff}
1 2 3
var keys: CharRange = 'a'..'f' val map: Map<Char, Pair<Char, String>> = keys.associateWith { it to it.toString().repeat(5).capitalize() } println(map) // {a=(a, Aaaaa), b=(b, Bbbbb), c=(c, Ccccc), d=(d, Ddddd), e=(e, Eeeee), f=(f, Fffff)}
contains
key, value 에 요소가 포함되어 있는지 검사하는 메서드를 제공한다.
1 2 3 4 5
val map = mapOf("hello" to "world", "foo" to "bar") println(map.contains("hello")) // true, containsKey 와 동일 println(map.containsKey("hello")) // true println(map.containsValue("bar")) // true println("hello"in map) // true
구조분해, 오버라이딩 함수
Pair 콜렉션을 기반으로 반복문을 구성하는데 Pair 의 구조분해 문법과 같이 사용할 수 있다.
1 2 3 4 5 6 7 8 9
val mmap = mutableMapOf("hello" to "world", "foo" to "bar") for (entry in mmap) { println("${entry.key} : ${entry.value}") } for ((k, v) in mmap) { println("$k : $v") } // hello : world // foo : bar
List 콜렉션과 마찬가지로 Map 콜렉션에서도 오버라이딩 함수를 제공한다.
1 2 3 4 5
val map = mapOf("hello" to "world", "foo" to "bar") var newMap1 = map + ("baz" to "quz") println(newMap) // {hello=world, foo=bar, baz=quz} var newMap2= map - "hello" println(newMap2) // {foo=bar}
내부 반복자 메서드
kotlin 에서는 [filter, map] 과 같은 콜렉션을 쉽게 컨트롤할 수 있는 내부 반복자 메서드를 제공한다, 내부 반복자 메서드 는 즉시 처리되어 모든원소에 접근한다.
java 에선 성능상의 이유로 stream 을 통해 해당 함수를 호출시켰지만, kotlin 에선 편의성을 위해 바로 사용가능하다.
funmain() { val list: List<Any> = listOf("a", LocalDate.now(), 3, 1, 4, "b") val strings: List<Any> = list.filter { it is String } for (str in strings) { str.length // error, Any 타입에는 length 가 없음 } }
filterIsInstance 를 사용해 해당 요소만 뽑아내어 타입을 지정할 수 있다.
1 2 3 4 5
val list: List<Any> = listOf("a", LocalDate.now(), 3, 1, 4, "b") val all: List<Any> = list.filterIsInstance<Any>() val strings: List<String> = list.filterIsInstance<String>() val ints: List<Int> = list.filterIsInstance<Int>() val dates: List<LocalDate> = list.filterIsInstance(LocalDate::class.java)
사실 filterIsInstance 은 filterIsInstanceTo 함수를 구현한 함수이며 filterIsInstanceTo 를 사용하면 어떤 인스턴스에 해당 요소를 집어넣을지 지정 가능하다.
1 2 3 4 5
val list: List<Any> = listOf("a", LocalDate.now(), 3, 1, 4, "b") val all: MutableList<Any> = list.filterIsInstanceTo(mutableListOf()) val strings: MutableList<String> = list.filterIsInstanceTo(mutableListOf<String>()) val ints: MutableList<Int> = list.filterIsInstanceTo(mutableListOf<Int>()) val dates: MutableList<LocalDate> = list.filterIsInstanceTo(mutableListOf<LocalDate>())
범위클래스를 상속하면 .. 키워드를 사용해 범위타입 클래스인 ClosedRange 을 사용할 수 있다.
1 2
val range1: CharRange = 'a'..'f' val range2: CharRange = 'a'.rangeTo('f')
범위클래스로 CharRange 외에도 IntRange, LongRange 등이 있으며 모두 ClosedRange 를 구현하고 있다.
ClosedRange 의 contains 메서드를 보면 infix 연산자로 비교문만 정의하면 아래와 같이 in 키워드를 사용할 수 있다.
1 2 3 4 5 6 7 8 9 10
publicinterfaceClosedRange<T: Comparable<T>> { // The minimum value in the range. publicval start: T // The maximum value in the range (inclusive). publicval endInclusive: T // Checks whether the specified [value] belongs to the range. publicoperatorfuncontains(value: T): Boolean = value >= start && value <= endInclusive // Checks whether the range is empty. publicfunisEmpty(): Boolean = start > endInclusive }
1 2 3 4 5 6 7 8 9 10
val startDate = LocalDate.now() val midDate = startDate.plusDays(3) val endDate = startDate.plusDays(5) val overDate = startDate.plusDays(6)
val dateRange: ClosedRange<LocalDate> = startDate..endDate println(startDate in dateRange) // true println(midDate in dateRange) // true println(endDate in dateRange) // true println(overDate in dateRange) // false
Progression(수열, 연속)
ClosedRange<LocalDate> 타입이 수열처럼 연속된 값의 나열은 아니기 때문에 for...each 사용은 불가능하다.