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

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

이 글의 핵심

Kotlin 컬렉션: List, Set, Map List·Set.

들어가며

읽기 전용가변 컬렉션을 나누어 두면, 의도치 않은 수정을 줄이기 좋습니다. map·filter 등은 새 리스트를 돌려주는 스타일이 기본에 가깝습니다.

실무에서 마주한 현실

개발을 배울 때는 모든 게 깔끔하고 이론적입니다. 하지만 실무는 다릅니다. 레거시 코드와 씨름하고, 급한 일정에 쫓기고, 예상치 못한 버그와 마주합니다. 이 글에서 다루는 내용도 처음엔 이론으로 배웠지만, 실제 프로젝트에 적용하면서 “아, 이래서 이렇게 설계하는구나” 하고 깨달은 것들입니다. 특히 기억에 남는 건 첫 프로젝트에서 겪은 시행착오입니다. 책에서 배운 대로 했는데 왜 안 되는지 몰라 며칠을 헤맸죠. 결국 선배 개발자의 코드 리뷰를 통해 문제를 발견했고, 그 과정에서 많은 걸 배웠습니다. 이 글에서는 이론뿐 아니라 실전에서 마주칠 수 있는 함정들과 해결 방법을 함께 다루겠습니다.

1. List

읽기 전용 List

아래 코드는 kotlin를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

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

아래 코드는 kotlin를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

val mutableFruits = mutableListOf("사과", "바나나")
// 추가
mutableFruits.add("오렌지")
mutableFruits.add(0, "포도")  // 인덱스 지정
// 삭제
mutableFruits.remove("사과")
mutableFruits.removeAt(0)
// 수정
mutableFruits[0] = "딸기"
println(mutableFruits)  // [딸기, 바나나, 오렌지]

List 생성 방법

아래 코드는 kotlin를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// 빈 리스트
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

아래 코드는 kotlin를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

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

아래 코드는 kotlin를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

val mutableNumbers = mutableSetOf(1, 2, 3)
mutableNumbers.add(4)
mutableNumbers.add(2)  // 중복은 추가 안됨
mutableNumbers.remove(1)
println(mutableNumbers)  // [2, 3, 4]

3. Map

읽기 전용 Map

다음은 kotlin를 활용한 상세한 구현 코드입니다. 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

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

아래 코드는 kotlin를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

val mutableAges = mutableMapOf("홍길동" to 25)
// 추가/수정
mutableAges[김철수] = 30
mutableAges.put("이영희", 28)
// 삭제
mutableAges.remove("홍길동")
// 조건부 추가
mutableAges.putIfAbsent("박민수", 35)
println(mutableAges)

4. 컬렉션 연산

filter (필터링)

아래 코드는 kotlin를 사용한 구현 예제입니다. 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

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 (변환)

아래 코드는 kotlin를 사용한 구현 예제입니다. 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

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

아래 코드는 kotlin를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

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 (그룹화)

아래 코드는 kotlin를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

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 (분할)

아래 코드는 kotlin를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

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

아래 코드는 kotlin를 사용한 구현 예제입니다. 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

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

아래 코드는 kotlin를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

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

아래 코드는 kotlin를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

val fruits = listOf("사과", "바나나", "오렌지")
// 인덱스를 키로
val indexed = fruits.associateBy { it.first() }
println(indexed)  // {사=사과, 바=바나나, 오=오렌지}
// 커스텀 키-값
val lengths = fruits.associateWith { it.length }
println(lengths)  // {사과=2, 바나나=3, 오렌지=3}

6. Sequence (지연 평가)

아래 코드는 kotlin를 사용한 구현 예제입니다. 반복문으로 데이터를 처리합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

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: 사용자 필터링

다음은 kotlin를 활용한 상세한 구현 코드입니다. 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

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: 점수 통계

다음은 kotlin를 활용한 상세한 구현 코드입니다. 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

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: 단어 빈도수

아래 코드는 kotlin를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

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: 지연 평가

다음 단계


관련 글

... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3