컬렉션
컬렉션 (Collections)
프로그래밍을 하다 보면 나중에 처리하기 위해 데이터를 구조로 묶어 두는 게 유용해요. Kotlin은 정확히 이런 목적을 위해 컬렉션(collection)을 제공해요.
Kotlin에는 항목을 묶기 위한 컬렉션이 이렇게 있어요:
| 컬렉션 타입 | 설명 |
|---|---|
| 리스트 (Lists) | 항목의 순서가 있는 컬렉션이에요. |
| 셋 (Sets) | 항목이 중복되지 않는 순서 없는 컬렉션이에요. |
| 맵 (Maps) | 키가 유일해 하나의 값에만 대응하는 키-값 쌍의 모음이에요. |
각 컬렉션 타입은 변경 가능(mutable)하거나 읽기 전용(read only)일 수 있어요.
출처: Kotlin 공식 문서
본문
리스트 (List)
리스트(list)는 항목을 추가된 순서대로 저장하고, 중복 항목을 허용해요.
읽기 전용 리스트(List)를 만들려면 listOf() 함수를 사용해요.
변경 가능한 리스트(MutableList)를 만들려면 mutableListOf() 함수를 사용해요.
리스트를 만들 때 Kotlin은 저장되는 항목의 타입을 추론할 수 있어요. 타입을 명시적으로 선언하려면 리스트 선언 뒤 꺾쇠 괄호 <> 안에 타입을 넣어요:
fun main() {
//sampleStart
// Read only list
val readOnlyShapes = listOf("triangle", "square", "circle")
println(readOnlyShapes)
// [triangle, square, circle]
// Mutable list with explicit type declaration
val shapes: MutableList<String> = mutableListOf("triangle", "square", "circle")
println(shapes)
// [triangle, square, circle]
//sampleEnd
}
val shapes: MutableList<String> = mutableListOf("triangle", "square", "circle")
val shapesLocked: List<String> = shapes
Tip: 원하지 않는 수정을 막으려면 변경 가능한 리스트를
List에 할당해서 읽기 전용 뷰를 만들 수 있어요. 이것을 캐스팅(casting)이라고도 불러요.
리스트는 순서가 있으므로 리스트 안의 항목에 접근하려면 인덱스 접근 연산자(indexed access operator) []를 사용해요:
fun main() {
//sampleStart
val readOnlyShapes = listOf("triangle", "square", "circle")
println("The first item in the list is: ${readOnlyShapes[0]}")
// The first item in the list is: triangle
//sampleEnd
}
리스트의 첫 번째나 마지막 항목을 가져오려면 각각 .first()와 .last() 함수를 사용해요:
fun main() {
//sampleStart
val readOnlyShapes = listOf("triangle", "square", "circle")
println("The first item in the list is: ${readOnlyShapes.first()}")
// The first item in the list is: triangle
//sampleEnd
}
Note:
.first()와.last()함수는 확장 함수(extension function)의 예시예요. 객체에서 확장 함수를 호출하려면 객체 뒤에 마침표.를 붙인 다음 함수 이름을 써요. 확장 함수는 중급 투어(intermediate tour)에서 자세히 다뤄요. 지금은 호출하는 방법만 알면 돼요.
리스트의 항목 개수를 얻으려면 .count() 함수를 사용해요:
fun main() {
//sampleStart
val readOnlyShapes = listOf("triangle", "square", "circle")
println("This list has ${readOnlyShapes.count()} items")
// This list has 3 items
//sampleEnd
}
항목이 리스트에 있는지 확인하려면 in 연산자를 사용해요:
fun main() {
//sampleStart
val readOnlyShapes = listOf("triangle", "square", "circle")
println("circle" in readOnlyShapes)
// true
//sampleEnd
}
변경 가능한 리스트에 항목을 추가하거나 제거하려면 각각 .add()와 .remove() 함수를 사용해요:
fun main() {
//sampleStart
val shapes: MutableList<String> = mutableListOf("triangle", "square", "circle")
// Add "pentagon" to the list
shapes.add("pentagon")
println(shapes)
// [triangle, square, circle, pentagon]
// Remove the first "pentagon" from the list
shapes.remove("pentagon")
println(shapes)
// [triangle, square, circle]
//sampleEnd
}
셋 (Set)
리스트가 순서가 있고 중복 항목을 허용하는 반면, 셋(set)은 순서가 없고 고유 항목만 저장해요.
읽기 전용 셋(Set)을 만들려면 setOf() 함수를 사용해요.
변경 가능한 셋(MutableSet)을 만들려면 mutableSetOf() 함수를 사용해요.
셋을 만들 때 Kotlin은 저장되는 항목의 타입을 추론할 수 있어요. 타입을 명시적으로 선언하려면 셋 선언 뒤 꺾쇠 괄호 <> 안에 타입을 넣어요:
fun main() {
//sampleStart
// Read-only set
val readOnlyFruit = setOf("apple", "banana", "cherry", "cherry")
// Mutable set with explicit type declaration
val fruit: MutableSet<String> = mutableSetOf("apple", "banana", "cherry", "cherry")
println(readOnlyFruit)
// [apple, banana, cherry]
//sampleEnd
}
앞선 예시에서 볼 수 있듯이 셋은 고유 요소만 담기 때문에 중복인 "cherry" 항목은 버려져요.
val fruit: MutableSet<String> = mutableSetOf("apple", "banana", "cherry", "cherry")
val fruitLocked: Set<String> = fruit
Tip: 원하지 않는 수정을 막으려면 변경 가능한 셋을
Set에 할당해서 읽기 전용 뷰를 만들 수 있어요.
Note: 셋은 순서가 없으므로 특정 인덱스의 항목에 접근할 수 없어요.
셋의 항목 개수를 얻으려면 .count() 함수를 사용해요:
fun main() {
//sampleStart
val readOnlyFruit = setOf("apple", "banana", "cherry", "cherry")
println("This set has ${readOnlyFruit.count()} items")
// This set has 3 items
//sampleEnd
}
항목이 셋에 있는지 확인하려면 in 연산자를 사용해요:
fun main() {
//sampleStart
val readOnlyFruit = setOf("apple", "banana", "cherry", "cherry")
println("banana" in readOnlyFruit)
// true
//sampleEnd
}
변경 가능한 셋에 항목을 추가하거나 제거하려면 각각 .add()와 .remove() 함수를 사용해요:
fun main() {
//sampleStart
val fruit: MutableSet<String> = mutableSetOf("apple", "banana", "cherry", "cherry")
fruit.add("dragonfruit") // Add "dragonfruit" to the set
println(fruit) // [apple, banana, cherry, dragonfruit]
fruit.remove("dragonfruit") // Remove "dragonfruit" from the set
println(fruit) // [apple, banana, cherry]
//sampleEnd
}
맵 (Map)
맵(map)은 항목을 키-값 쌍으로 저장해요. 키를 참조해서 값을 얻는 방식이에요. 맵을 음식 메뉴처럼 생각해 볼 수 있어요. 먹고 싶은 음식(키)을 찾으면 가격(값)을 알 수 있죠. 리스트처럼 번호가 붙은 인덱스 없이 값을 찾고 싶을 때 맵이 유용해요.
Note: Kotlin이 어떤 값을 가져올지 알 수 있도록 맵의 모든 키는 유일해야 해요. 맵에는 중복된 값이 있어도 돼요.
- 읽기 전용 맵(
Map)을 만들려면mapOf()함수를 사용해요.
변경 가능한 맵(MutableMap)을 만들려면 mutableMapOf() 함수를 사용해요.
맵을 만들 때 Kotlin은 저장되는 항목의 타입을 추론할 수 있어요. 타입을 명시적으로 선언하려면 맵 선언 뒤 꺾쇠 괄호 <> 안에 키와 값의 타입을 넣어요. 예를 들어: MutableMap<String, Int>. 키는 String 타입이고 값은 Int 타입이에요.
맵을 만드는 가장 쉬운 방법은 각 키와 그에 대응하는 값 사이에 to를 사용하는 거예요:
fun main() {
//sampleStart
// Read-only map
val readOnlyJuiceMenu = mapOf("apple" to 100, "kiwi" to 190, "orange" to 100)
println(readOnlyJuiceMenu)
// {apple=100, kiwi=190, orange=100}
// Mutable map with explicit type declaration
val juiceMenu: MutableMap<String, Int> = mutableMapOf("apple" to 100, "kiwi" to 190, "orange" to 100)
println(juiceMenu)
// {apple=100, kiwi=190, orange=100}
//sampleEnd
}
val juiceMenu: MutableMap<String, Int> = mutableMapOf("apple" to 100, "kiwi" to 190, "orange" to 100)
val juiceMenuLocked: Map<String, Int> = juiceMenu
Tip: 원하지 않는 수정을 막으려면 변경 가능한 맵을
Map에 할당해서 읽기 전용 뷰를 만들 수 있어요.
맵의 값을 얻으려면 키와 함께 인덱스 접근 연산자 []를 사용해요:
fun main() {
//sampleStart
// Read-only map
val readOnlyJuiceMenu = mapOf("apple" to 100, "kiwi" to 190, "orange" to 100)
println("The value of apple juice is: ${readOnlyJuiceMenu["apple"]}")
// The value of apple juice is: 100
//sampleEnd
}
fun main() {
//sampleStart
// Read-only map
val readOnlyJuiceMenu = mapOf("apple" to 100, "kiwi" to 190, "orange" to 100)
println("The value of pineapple juice is: ${readOnlyJuiceMenu["pineapple"]}")
// The value of pineapple juice is: null
//sampleEnd
}
Note: 맵에 존재하지 않는 키로 키-값 쌍에 접근하려고 하면
null값이 보여요. 이 투어의 Null safety 챕터에서 null 값에 대해 나중에 설명할게요.
변경 가능한 맵에 항목을 추가할 때도 인덱스 접근 연산자 []를 사용할 수 있어요:
fun main() {
//sampleStart
val juiceMenu: MutableMap<String, Int> = mutableMapOf("apple" to 100, "kiwi" to 190, "orange" to 100)
juiceMenu["coconut"] = 150 // Add key "coconut" with value 150 to the map
println(juiceMenu)
// {apple=100, kiwi=190, orange=100, coconut=150}
//sampleEnd
}
변경 가능한 맵에서 항목을 제거하려면 .remove() 함수를 사용해요:
fun main() {
//sampleStart
val juiceMenu: MutableMap<String, Int> = mutableMapOf("apple" to 100, "kiwi" to 190, "orange" to 100)
juiceMenu.remove("orange") // Remove key "orange" from the map
println(juiceMenu)
// {apple=100, kiwi=190}
//sampleEnd
}
맵의 항목 개수를 얻으려면 .count() 함수를 사용해요:
fun main() {
//sampleStart
// Read-only map
val readOnlyJuiceMenu = mapOf("apple" to 100, "kiwi" to 190, "orange" to 100)
println("This map has ${readOnlyJuiceMenu.count()} key-value pairs")
// This map has 3 key-value pairs
//sampleEnd
}
맵에 특정 키가 이미 포함되어 있는지 확인하려면 .containsKey() 함수를 사용해요:
fun main() {
//sampleStart
val readOnlyJuiceMenu = mapOf("apple" to 100, "kiwi" to 190, "orange" to 100)
println(readOnlyJuiceMenu.containsKey("kiwi"))
// true
//sampleEnd
}
맵의 키나 값의 컬렉션을 얻으려면 각각 keys와 values 프로퍼티를 사용해요:
fun main() {
//sampleStart
val readOnlyJuiceMenu = mapOf("apple" to 100, "kiwi" to 190, "orange" to 100)
println(readOnlyJuiceMenu.keys)
// [apple, kiwi, orange]
println(readOnlyJuiceMenu.values)
// [100, 190, 100]
//sampleEnd
}
Note:
keys와values는 객체의 프로퍼티(property) 예시예요. 객체의 프로퍼티에 접근하려면 객체 뒤에 마침표.를 붙인 다음 프로퍼티 이름을 써요. 프로퍼티는 Classes 챕터에서 더 자세히 다뤄요. 투어의 이 시점에서는 접근하는 방법만 알면 돼요.
키나 값이 맵에 있는지 확인하려면 in 연산자를 사용해요:
fun main() {
//sampleStart
val readOnlyJuiceMenu = mapOf("apple" to 100, "kiwi" to 190, "orange" to 100)
println("orange" in readOnlyJuiceMenu.keys)
// true
// Alternatively, you don't need to use the keys property
println("orange" in readOnlyJuiceMenu)
// true
println(200 in readOnlyJuiceMenu.values)
// false
//sampleEnd
}
컬렉션으로 할 수 있는 일에 대한 자세한 내용은 Collections 문서를 참고하세요.
이제 기본 타입과 컬렉션을 다루는 방법을 알았으니, 프로그램에서 쓸 수 있는 제어 흐름(control flow)을 살펴볼 차례예요.
연습 (Practice)
연습 1 (Exercise 1)
"초록" 숫자 리스트와 "빨강" 숫자 리스트가 있어요. 총 숫자가 몇 개인지 출력하도록 코드를 완성하세요.
fun main() {
val greenNumbers = listOf(1, 4, 23)
val redNumbers = listOf(17, 2)
// Write your code here
}
fun main() {
val greenNumbers = listOf(1, 4, 23)
val redNumbers = listOf(17, 2)
val totalCount = greenNumbers.count() + redNumbers.count()
println(totalCount)
}
연습 2 (Exercise 2)
서버가 지원하는 프로토콜의 셋(set)이 있어요. 사용자가 특정 프로토콜을 사용하기를 요청해요. 요청한 프로토콜이 지원되는지 여부를 확인하는 프로그램을 완성하세요(isSupported는 반드시 Boolean 값이어야 해요).
fun main() {
val SUPPORTED = setOf("HTTP", "HTTPS", "FTP")
val requested = "smtp"
val isSupported = // Write your code here
println("Support for $requested: $isSupported")
}
Hint: 요청한 프로토콜을 대문자로 확인했는지 확인하세요. .uppercase() 함수를 쓰면 도움이 돼요.
fun main() {
val SUPPORTED = setOf("HTTP", "HTTPS", "FTP")
val requested = "smtp"
val isSupported = requested.uppercase() in SUPPORTED
println("Support for $requested: $isSupported")
}
연습 3 (Exercise 3)
1부터 3까지의 정수와 그에 대응하는 철자를 연결하는 맵을 정의하세요. 이 맵을 사용해서 주어진 숫자를 철자로 표기하세요.
fun main() {
val number2word = // Write your code here
val n = 2
println("$n is spelled as '${<Write your code here >}'")
}
fun main() {
val number2word = mapOf(1 to "one", 2 to "two", 3 to "three")
val n = 2
println("$n is spelled as '${number2word[n]}'")
}