Kotlin 컬렉션 | List, Set, Map 완벽 정리
이 글의 핵심
Kotlin 컬렉션에 대한 실전 가이드입니다. List, Set, Map 완벽 정리 등을 예제와 함께 상세히 설명합니다.
들어가며
읽기 전용과 가변 컬렉션을 나누어 두면, 의도치 않은 수정을 줄이기 좋습니다. map·filter 등은 새 리스트를 돌려주는 스타일이 기본에 가깝습니다.
1. List
읽기 전용 List
val fruits = listOf("사과", "바나나", "오렌지")
// 접근
println(fruits[0]) // 사과
println(fruits.first()) // 사과
println(fruits.last()) // 오렌지
println(fruits.size) // 3
// 검색
println(fruits.contains("사과")) // true
println(fruits.indexOf("바나나")) // 1
변경 가능 MutableList
val mutableFruits = mutableListOf("사과", "바나나")
// 추가
mutableFruits.add("오렌지")
mutableFruits.add(0, "포도") // 인덱스 지정
// 삭제
mutableFruits.remove("사과")
mutableFruits.removeAt(0)
// 수정
mutableFruits[0] = "딸기"
println(mutableFruits) // [딸기, 바나나, 오렌지]
List 생성 방법
// 빈 리스트
val empty = emptyList<String>()
val mutableEmpty = mutableListOf<String>()
// 크기 지정
val zeros = List(5) { 0 } // [0, 0, 0, 0, 0]
val squares = List(5) { it * it } // [0, 1, 4, 9, 16]
// 범위로 생성
val numbers = (1..10).toList()
2. Set
읽기 전용 Set
val numbers = setOf(1, 2, 3, 2, 1)
println(numbers) // [1, 2, 3] (중복 제거)
// 집합 연산
val set1 = setOf(1, 2, 3)
val set2 = setOf(2, 3, 4)
println(set1 union set2) // [1, 2, 3, 4]
println(set1 intersect set2) // [2, 3]
println(set1 subtract set2) // [1]
변경 가능 MutableSet
val mutableNumbers = mutableSetOf(1, 2, 3)
mutableNumbers.add(4)
mutableNumbers.add(2) // 중복은 추가 안됨
mutableNumbers.remove(1)
println(mutableNumbers) // [2, 3, 4]
3. Map
읽기 전용 Map
val ages = mapOf(
"홍길동" to 25,
"김철수" to 30,
"이영희" to 28
)
// 접근
println(ages["홍길동"]) // 25
println(ages.get("홍길동")) // 25
println(ages.getOrDefault("박민수", 0)) // 0
// 키/값 확인
println(ages.containsKey("홍길동")) // true
println(ages.containsValue(25)) // true
// 순회
ages.forEach { (name, age) ->
println("$name: $age세")
}
변경 가능 MutableMap
val mutableAges = mutableMapOf("홍길동" to 25)
// 추가/수정
mutableAges["김철수"] = 30
mutableAges.put("이영희", 28)
// 삭제
mutableAges.remove("홍길동")
// 조건부 추가
mutableAges.putIfAbsent("박민수", 35)
println(mutableAges)
4. 컬렉션 연산
filter (필터링)
val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
// 짝수만
val evens = numbers.filter { it % 2 == 0 }
println(evens) // [2, 4, 6, 8, 10]
// 5보다 큰 수
val greaterThan5 = numbers.filter { it > 5 }
println(greaterThan5) // [6, 7, 8, 9, 10]
// filterNot
val odds = numbers.filterNot { it % 2 == 0 }
println(odds) // [1, 3, 5, 7, 9]
map (변환)
val numbers = listOf(1, 2, 3, 4, 5)
// 2배
val doubled = numbers.map { it * 2 }
println(doubled) // [2, 4, 6, 8, 10]
// 제곱
val squared = numbers.map { it * it }
println(squared) // [1, 4, 9, 16, 25]
// 문자열 변환
val strings = numbers.map { "숫자: $it" }
println(strings) // [숫자: 1, 숫자: 2, ...]
reduce와 fold
val numbers = listOf(1, 2, 3, 4, 5)
// reduce (첫 번째 요소가 초기값)
val sum = numbers.reduce { acc, num -> acc + num }
println(sum) // 15
// fold (초기값 지정)
val sum2 = numbers.fold(0) { acc, num -> acc + num }
println(sum2) // 15
val product = numbers.fold(1) { acc, num -> acc * num }
println(product) // 120
groupBy (그룹화)
val words = listOf("apple", "banana", "avocado", "berry", "cherry")
// 첫 글자로 그룹화
val grouped = words.groupBy { it.first() }
println(grouped)
// {a=[apple, avocado], b=[banana, berry], c=[cherry]}
// 길이로 그룹화
val byLength = words.groupBy { it.length }
println(byLength)
// {5=[apple, berry], 6=[banana, cherry], 7=[avocado]}
partition (분할)
val numbers = listOf(1, 2, 3, 4, 5, 6)
val (evens, odds) = numbers.partition { it % 2 == 0 }
println(evens) // [2, 4, 6]
println(odds) // [1, 3, 5]
5. 고급 연산
flatMap
val lists = listOf(
listOf(1, 2, 3),
listOf(4, 5),
listOf(6, 7, 8)
)
val flattened = lists.flatMap { it }
println(flattened) // [1, 2, 3, 4, 5, 6, 7, 8]
// 변환 + 평탄화
val doubled = lists.flatMap { list -> list.map { it * 2 } }
println(doubled) // [2, 4, 6, 8, 10, 12, 14, 16]
zip
val names = listOf("홍길동", "김철수", "이영희")
val ages = listOf(25, 30, 28)
val pairs = names.zip(ages)
println(pairs) // [(홍길동, 25), (김철수, 30), (이영희, 28)]
// 커스텀 변환
val users = names.zip(ages) { name, age -> "$name ($age세)" }
println(users) // [홍길동 (25세), 김철수 (30세), 이영희 (28세)]
associate
val fruits = listOf("사과", "바나나", "오렌지")
// 인덱스를 키로
val indexed = fruits.associateBy { it.first() }
println(indexed) // {사=사과, 바=바나나, 오=오렌지}
// 커스텀 키-값
val lengths = fruits.associateWith { it.length }
println(lengths) // {사과=2, 바나나=3, 오렌지=3}
6. Sequence (지연 평가)
val numbers = (1..1000000).asSequence()
.filter { it % 2 == 0 }
.map { it * 2 }
.take(5)
.toList()
println(numbers) // [4, 8, 12, 16, 20]
List vs Sequence:
| 특징 | List | Sequence |
|---|---|---|
| 평가 | 즉시 | 지연 |
| 성능 | 작은 데이터 | 대용량 데이터 |
| 메모리 | 모두 저장 | 필요한 것만 |
7. 실전 예제
예제 1: 사용자 필터링
data class User(val name: String, val age: Int, val city: String)
fun main() {
val users = listOf(
User("홍길동", 25, "서울"),
User("김철수", 30, "부산"),
User("이영희", 28, "서울"),
User("박민수", 35, "서울"),
User("최영수", 22, "대구")
)
// 서울에 사는 30세 미만
val result = users
.filter { it.city == "서울" }
.filter { it.age < 30 }
.map { it.name }
println(result) // [홍길동, 이영희]
// 도시별 그룹화
val byCity = users.groupBy { it.city }
byCity.forEach { (city, users) ->
println("$city: ${users.map { it.name }}")
}
}
예제 2: 점수 통계
fun main() {
val scores = mapOf(
"홍길동" to 85,
"김철수" to 92,
"이영희" to 78,
"박민수" to 95,
"최영수" to 88
)
// 평균
val average = scores.values.average()
println("평균: $average")
// 최고점
val maxScore = scores.maxByOrNull { it.value }
println("최고점: ${maxScore?.key} (${maxScore?.value}점)")
// 80점 이상
val passed = scores.filter { it.value >= 80 }
println("합격: ${passed.keys}")
}
예제 3: 단어 빈도수
fun main() {
val text = "apple banana apple cherry banana apple"
val words = text.split(" ")
// 단어 빈도수
val frequency = words.groupingBy { it }.eachCount()
println(frequency)
// {apple=3, banana=2, cherry=1}
// 가장 많이 나온 단어
val mostFrequent = frequency.maxByOrNull { it.value }
println("가장 많은 단어: ${mostFrequent?.key} (${mostFrequent?.value}번)")
}
정리
핵심 요약
- List: 순서 있는 컬렉션
- Set: 중복 없는 컬렉션
- Map: 키-값 쌍
- 연산: filter, map, reduce, groupBy
- Sequence: 지연 평가
다음 단계
- Kotlin 코루틴
- Kotlin Android 개발
- Kotlin 테스팅
관련 글
- C++ 컨테이너 선택 가이드 | vector/list/deque/map/set 상황별 선택과 성능 최적화
- Python 자료형 | 리스트, 딕셔너리, 튜플, 세트 완벽 가이드
- C++ Algorithm Set |
- C++ vector vs list vs deque |
- C++ map vs unordered_map 심층 비교 |