Notice
Recent Posts
Recent Comments
Link
250x250
반응형
«   2025/12   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
Archives
Today
Total
관리 메뉴

백고등어 개발 블로그

코틀린 강의 6강: 컬렉션 본문

코틀린 강의

코틀린 강의 6강: 컬렉션

백고등어 2025. 10. 28. 17:38
728x90
반응형

6강: 컬렉션

컬렉션 개요

코틀린은 자바의 컬렉션을 기반으로 하되, 불변(immutable)과 가변(mutable) 컬렉션을 명확히 구분합니다. 이는 함수형 프로그래밍 스타일을 지원하고 안전성을 높이기 위함입니다.

List

불변 리스트(Immutable List)

// listOf: 읽기 전용 리스트 생성
val fruits = listOf("사과", "바나나", "오렌지")
println(fruits[0])        // 사과
println(fruits.size)      // 3
println(fruits.first())   // 사과
println(fruits.last())    // 오렌지

// fruits.add("포도")  // 컴파일 오류! 불변 리스트

// 빈 리스트
val emptyList = emptyList<String>()

가변 리스트(Mutable List)

// mutableListOf: 수정 가능한 리스트 생성
val numbers = mutableListOf(1, 2, 3, 4, 5)

// 요소 추가
numbers.add(6)
numbers.add(0, 0)  // 인덱스 0에 0 삽입

// 요소 제거
numbers.remove(3)
numbers.removeAt(0)

// 요소 수정
numbers[0] = 10

println(numbers)  // [10, 2, 4, 5, 6]

리스트 연산

val numbers = listOf(1, 2, 3, 4, 5)

// 검색
println(2 in numbers)        // true
println(numbers.contains(6)) // false
println(numbers.indexOf(3))  // 2

// 정렬
println(numbers.sorted())              // [1, 2, 3, 4, 5]
println(numbers.sortedDescending())    // [5, 4, 3, 2, 1]

// 필터링과 변환
val evens = numbers.filter { it % 2 == 0 }
println(evens)  // [2, 4]

val doubled = numbers.map { it * 2 }
println(doubled)  // [2, 4, 6, 8, 10]

// 슬라이싱
println(numbers.subList(1, 4))  // [2, 3, 4]
println(numbers.take(3))        // [1, 2, 3]
println(numbers.drop(2))        // [3, 4, 5]

Set

중복을 허용하지 않는 컬렉션입니다.

불변 Set

val fruits = setOf("사과", "바나나", "사과", "오렌지")
println(fruits)  // [사과, 바나나, 오렌지] (중복 제거됨)
println(fruits.size)  // 3

가변 Set

val numbers = mutableSetOf(1, 2, 3)

numbers.add(4)
numbers.add(2)  // 이미 존재하므로 추가되지 않음
numbers.remove(1)

println(numbers)  // [2, 3, 4]

Set 연산

val set1 = setOf(1, 2, 3, 4)
val set2 = setOf(3, 4, 5, 6)

// 합집합
println(set1 union set2)        // [1, 2, 3, 4, 5, 6]
println(set1 + set2)            // [1, 2, 3, 4, 5, 6]

// 교집합
println(set1 intersect set2)    // [3, 4]

// 차집합
println(set1 - set2)            // [1, 2]
println(set1 subtract set2)     // [1, 2]

Map

키-값 쌍을 저장하는 컬렉션입니다.

불변 Map

// mapOf: 읽기 전용 맵 생성
val capitals = mapOf(
    "한국" to "서울",
    "일본" to "도쿄",
    "중국" to "베이징"
)

println(capitals["한국"])      // 서울
println(capitals.get("일본"))  // 도쿄
println(capitals["미국"])      // null

// getOrDefault
println(capitals.getOrDefault("미국", "알 수 없음"))  // 알 수 없음

// keys와 values
println(capitals.keys)    // [한국, 일본, 중국]
println(capitals.values)  // [서울, 도쿄, 베이징]

가변 Map

val scores = mutableMapOf(
    "김철수" to 85,
    "이영희" to 92
)

// 요소 추가/수정
scores["박민수"] = 78
scores["김철수"] = 90  // 기존 값 수정

// 요소 제거
scores.remove("이영희")

// 여러 요소 추가
scores.putAll(mapOf("최지원" to 88, "정수민" to 95))

println(scores)

Map 순회

val scores = mapOf("김철수" to 85, "이영희" to 92, "박민수" to 78)

// 키-값 쌍으로 순회
for ((name, score) in scores) {
    println("$name: $score점")
}

// 키만 순회
for (name in scores.keys) {
    println(name)
}

// 값만 순회
for (score in scores.values) {
    println(score)
}

// forEach 사용
scores.forEach { (name, score) ->
    println("$name: $score점")
}

컬렉션 변환 함수

map - 각 요소 변환

val numbers = listOf(1, 2, 3, 4, 5)
val squares = numbers.map { it * it }
println(squares)  // [1, 4, 9, 16, 25]

val names = listOf("alice", "bob", "charlie")
val upperNames = names.map { it.uppercase() }
println(upperNames)  // [ALICE, BOB, CHARLIE]

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]

val greaterThanFive = numbers.filter { it > 5 }
println(greaterThanFive)  // [6, 7, 8, 9, 10]

flatMap - 중첩 컬렉션 평탄화

val numbers = listOf(1, 2, 3)
val result = numbers.flatMap { listOf(it, it * 10) }
println(result)  // [1, 10, 2, 20, 3, 30]

val words = listOf("Hello", "World")
val letters = words.flatMap { it.toList() }
println(letters)  // [H, e, l, l, o, W, o, r, l, d]

groupBy - 그룹화

val words = listOf("apple", "banana", "avocado", "cherry", "blueberry")
val grouped = words.groupBy { it.first() }
println(grouped)
// {a=[apple, avocado], b=[banana, blueberry], c=[cherry]}

val numbers = listOf(1, 2, 3, 4, 5, 6)
val evenOdd = numbers.groupBy { if (it % 2 == 0) "even" else "odd" }
println(evenOdd)
// {odd=[1, 3, 5], even=[2, 4, 6]}

집계 함수

val numbers = listOf(1, 2, 3, 4, 5)

println(numbers.sum())       // 15
println(numbers.average())   // 3.0
println(numbers.max())       // 5
println(numbers.min())       // 1
println(numbers.count())     // 5

// 조건부 카운트
println(numbers.count { it % 2 == 0 })  // 2

// any, all, none
println(numbers.any { it > 3 })   // true
println(numbers.all { it > 0 })   // true
println(numbers.none { it < 0 })  // true

reduce와 fold

reduce - 누적 연산

val numbers = listOf(1, 2, 3, 4, 5)

val sum = numbers.reduce { acc, num -> acc + num }
println(sum)  // 15

val product = numbers.reduce { acc, num -> acc * num }
println(product)  // 120

fold - 초기값을 가진 누적 연산

val numbers = listOf(1, 2, 3, 4, 5)

val sum = numbers.fold(0) { acc, num -> acc + num }
println(sum)  // 15

val sumWithInitial = numbers.fold(100) { acc, num -> acc + num }
println(sumWithInitial)  // 115

// 문자열 누적
val words = listOf("코틀린", "은", "재미있다")
val sentence = words.fold("") { acc, word -> "$acc $word" }
println(sentence.trim())  // 코틀린 은 재미있다

시퀀스(Sequence)

대량의 데이터를 효율적으로 처리하기 위한 지연 평가(lazy evaluation) 컬렉션입니다.

// 일반 컬렉션: 중간 결과를 모두 생성
val numbers = (1..1000000).toList()
val result1 = numbers
    .map { it * 2 }      // 백만 개의 새 리스트 생성
    .filter { it > 5 }   // 또 다른 새 리스트 생성
    .take(10)

// 시퀀스: 필요한 만큼만 연산
val result2 = (1..1000000).asSequence()
    .map { it * 2 }      // 지연 평가
    .filter { it > 5 }   // 지연 평가
    .take(10)
    .toList()            // 이 시점에 10개만 실제 연산

// 무한 시퀀스
val infiniteSequence = generateSequence(1) { it + 1 }
val first100 = infiniteSequence.take(100).toList()
println(first100.last())  // 100

실전 예제

data class Student(val name: String, val grade: Int, val score: Int)

fun main() {
    val students = listOf(
        Student("김철수", 1, 85),
        Student("이영희", 2, 92),
        Student("박민수", 1, 78),
        Student("최지원", 2, 88),
        Student("정수민", 1, 95)
    )
    
    // 1학년 학생들의 평균 점수
    val grade1Average = students
        .filter { it.grade == 1 }
        .map { it.score }
        .average()
    println("1학년 평균: $grade1Average")
    
    // 학년별 그룹화
    val byGrade = students.groupBy { it.grade }
    byGrade.forEach { (grade, studentList) ->
        println("$grade학년: ${studentList.map { it.name }}")
    }
    
    // 최고 점수 학생
    val topStudent = students.maxByOrNull { it.score }
    println("최고 점수: ${topStudent?.name} - ${topStudent?.score}점")
    
    // 80점 이상 학생 수
    val passCount = students.count { it.score >= 80 }
    println("80점 이상: $passCount명")
}

마치며

이번 강의에서는 코틀린의 컬렉션과 다양한 변환 함수들에 대해 알아보았습니다. 불변 컬렉션을 우선 사용하고, map, filter 같은 함수형 프로그래밍 스타일의 함수들을 적극 활용하면 간결하고 안전한 코드를 작성할 수 있습니다. 다음 강의에서는 예외 처리에 대해 알아보겠습니다.

728x90
반응형