Files
kotlin-examples/docs/05_컬렉션.md
2025-02-22 01:33:42 +09:00

5.6 KiB

코틀린 컬렉션과 스트림 API vs 자바 스트림 API

코틀린은 자바와 마찬가지로 리스트(List), 세트(Set), 맵(Map) 등의 컬렉션을 제공한다. 하지만 자바와는 다르게, 컬렉션을 더욱 간결하게 다룰 수 있는 함수형 API를 기본 제공하며, 대부분의 연산을 람다식과 메서드 체이닝을 통해 쉽게 수행할 수 있다.

이번 글에서는 코틀린의 컬렉션 사용법과 자바 스트림 API와의 차이점을 비교해보겠다.


1. 코틀린 컬렉션 기본 개념

1.1. 불변(Immutable) vs 가변(Mutable) 컬렉션

코틀린의 컬렉션은 **불변형(listOf, setOf, mapOf)**과 **가변형(mutableListOf, mutableSetOf, mutableMapOf)**으로 나뉜다.
자바에서는 Collections.unmodifiableList() 등을 사용해야 하지만, 코틀린에서는 기본적으로 불변 컬렉션을 사용하도록 유도한다.

자바의 리스트 선언

List<String> immutableList = List.of("A", "B", "C");  // Java 9+
List<String> mutableList = new ArrayList<>();
mutableList.add("A");
mutableList.add("B");

코틀린의 리스트 선언

val immutableList = listOf("A", "B", "C")  // 불변 리스트
val mutableList = mutableListOf("A", "B")  // 가변 리스트
mutableList.add("C")

차이점 요약:

  • 자바: List.of()(불변) vs ArrayList<>()(가변)
  • 코틀린: listOf()(불변) vs mutableListOf()(가변)

2. 코틀린 컬렉션 사용 예제

2.1. 리스트(List)

val numbers = listOf(1, 2, 3, 4, 5)
println(numbers[0])  // 1
println(numbers.size)  // 5

가변 리스트:

val mutableNumbers = mutableListOf(1, 2, 3)
mutableNumbers.add(4)
println(mutableNumbers)  // [1, 2, 3, 4]

2.2. 집합(Set)

val names = setOf("Alice", "Bob", "Charlie")
println(names.contains("Alice"))  // true

가변 집합:

val mutableNames = mutableSetOf("Alice", "Bob")
mutableNames.add("Charlie")

2.3. 맵(Map)

val userMap = mapOf("Alice" to 25, "Bob" to 30)
println(userMap["Alice"])  // 25

가변 맵:

val mutableUserMap = mutableMapOf("Alice" to 25)
mutableUserMap["Bob"] = 30

3. 코틀린의 스트림 API vs 자바의 스트림 API

자바에서는 컬렉션을 처리할 때 **스트림 API(Stream)**를 사용해야 하지만,
코틀린은 컬렉션 자체가 스트림 API처럼 동작한다.

3.1. 자바 스트림 예제

List<String> names = List.of("Alice", "Bob", "Charlie");
List<String> filteredNames = names.stream()
    .filter(name -> name.startsWith("A"))
    .map(String::toUpperCase)
    .collect(Collectors.toList());

System.out.println(filteredNames);  // [ALICE]

자바의 특징:

  • .stream()을 호출해야 함
  • .collect(Collectors.toList())를 사용해야 리스트 반환

3.2. 코틀린 컬렉션 API 예제

val names = listOf("Alice", "Bob", "Charlie")
val filteredNames = names
    .filter { it.startsWith("A") }
    .map { it.uppercase() }

println(filteredNames)  // [ALICE]

코틀린의 특징:

  • stream() 없이 컬렉션 자체에서 메서드 체이닝 가능
  • collect() 없이 리스트 반환

3.3. 숫자 리스트 필터링 & 변환

자바 (Stream API 사용)

List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6);
List<Integer> evenSquares = numbers.stream()
    .filter(n -> n % 2 == 0)
    .map(n -> n * n)
    .collect(Collectors.toList());

System.out.println(evenSquares);  // [4, 16, 36]

코틀린 (컬렉션 API 사용)

val numbers = listOf(1, 2, 3, 4, 5, 6)
val evenSquares = numbers
    .filter { it % 2 == 0 }
    .map { it * it }

println(evenSquares)  // [4, 16, 36]

차이점 요약:

  • 자바: .stream() 호출 → filter(), map().collect(Collectors.toList()) 필요
  • 코틀린: 바로 filter(), map() 사용 가능

4. 추가적인 컬렉션 함수들

4.1. forEach

val numbers = listOf(1, 2, 3)
numbers.forEach { println(it) }  

4.2. groupBy

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

4.3. associateBy (키 매핑)

data class Person(val name: String, val age: Int)

val people = listOf(Person("Alice", 25), Person("Bob", 30))
val personMap = people.associateBy { it.name }
println(personMap["Alice"])  // Person(name=Alice, age=25)

5. 정리: 코틀린 vs 자바 스트림 API 차이점

기능 자바 (Stream API) 코틀린 (컬렉션 API)
스트림 사용 .stream() 필요 컬렉션 자체에서 사용 가능
리스트 반환 .collect(Collectors.toList()) 필요 자동 반환
filter() 사용 filter(n -> 조건) filter { it 조건 }
map() 사용 map(n -> 변환) map { it 변환 }
그룹핑 .collect(Collectors.groupingBy()) groupBy { it 기준 }
가변 리스트 new ArrayList<>() mutableListOf()

코틀린은 컬렉션을 더욱 직관적이고 간결하게 다룰 수 있도록 설계되었다.
자바에서 스트림 API를 써야 하는 많은 경우를 코틀린에서는 기본 컬렉션 함수만으로 해결할 수 있다.

코틀린으로 스트림 API를 더 쉽게 다룰 수 있다는 점을 기억하자!