백고등어 개발 블로그
코틀린 강의 6강: 컬렉션 본문
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
반응형
'코틀린 강의' 카테고리의 다른 글
| 코틀린 강의 8강: 제네릭 (0) | 2025.10.28 |
|---|---|
| 코틀린 강의 7강: 예외 처리 (0) | 2025.10.28 |
| 코틀린 강의 5강: 클래스와 객체 (0) | 2025.10.28 |
| 코틀린 강의 4강: 함수와 람다 표현식 (0) | 2025.10.28 |
| 코틀린 강의 3강: 연산자와 제어문 (0) | 2025.10.28 |