Kotlin 컬렉션 | List, Set, Map 완벽 정리

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:

특징ListSequence
평가즉시지연
성능작은 데이터대용량 데이터
메모리모두 저장필요한 것만

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}번)")
}

정리

핵심 요약

  1. List: 순서 있는 컬렉션
  2. Set: 중복 없는 컬렉션
  3. Map: 키-값 쌍
  4. 연산: filter, map, reduce, groupBy
  5. 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 심층 비교 |