Language/kotlin

배열과 컬렉션 다루기

kimjingyu 2023. 5. 23. 18:18

배열

  • array.indices는 0부터 마지막 index까지의 range이다.
  • withIndex()를 사용하면, 인덱스와 값을 한번에 가져올 수 있다.
  • array.plus()를 이용하면, 값을 쉽게 넣을 수도 있다.
val arr = arrayOf(100, 200)
arr.plus(300) // 배열에 새로운 element 추가

// 방법 1
for (i in arr.indices) {
    println("$i ${arr[i]}")
}

// 방법 2
for ((idx, value) in arr.withIndex()) {
    println("$idx $value")
}

Collection - List, Set, Map

  • 컬렉션을 만들어줄 때, 불변인지 혹은 가변인지를 설정해야 한다.
  • 가변(Mutable) 컬렉션: 컬렉션에 element를 추가, 삭제할 수 있다.
    • MutableList: 기본 구현체는 ArrayList이고, 기타 사용법은 자바와 동일하다.
    • MutableSet, MutableMap
  • 불변 컬렉션: 컬렉션에 element를 추가, 삭제할 수 없다.
    • List, Set, Map
    • Collection을 만들자마자 Collections.unmodifiableList() 등을 붙여준다.
    • 불변 컬렉션이라고 하더라도 Reference type인 element의 필드는 바꿀 수 있다.
  • 우선 불변 리스트를 만들고, 꼭 필요한 경우 가변 리스트로 바꾼다.

🔎 List

  • 가변 집합의 기본 구현체는 ArrayList이다.
//    val numbers = listOf(100, 200) // 불변 리스트
val numbers = mutableListOf(100, 200)
numbers.add(300)

val emptyList = emptyList<Int>()

useNumbers(emptyList()) // 타입 추론 가능시, 생략 가능

// 하나 가져오기
println(numbers[0])

// for each
for (number in numbers) {
    println("number = $number")
}

// 전통적인 for문
for ((index, value) in numbers.withIndex()) {
    println("index, value = $index, $value")
}

🔎 Set

  • 집합은 List와 다르게 순서가 없고, 같은 elemetn는 하나만 존재할 수 있다. (자료구조)
  • 가변 집합의 기본 구현체는 LinkedHashSet이다.
//    val numbers = setOf(100, 200) // 불변
val numbers = mutableSetOf(100, 200)    // 가변

// For Each
for (number in numbers) {
    println("number = $number")
}

// 전통적인 for문
for ((index, number) in numbers.withIndex()) {
    println("$index $number")
}

🔎 Map

  • 가변 Map을 사용하면 (key, value)를 넣을 수 있다.
  • 값을 넣을때는 자바처럼 put을 쓸 수도 있고, map[key] = value를 쓸 수도 있다.
  • 정적 팩토리 메서드 사용
    • 중위 호출 
    • mapOf(key to value)를 사용해 불변 map을 만들 수 있다.
// JDK 8까지
Map<Integer, String> map = new HashMap<>();
map.put(1, "MONDAY");
map.put(2, "TUESDAY");

// JDK 9부터
Map.of(1, "MONDAY", 2, "TUESDAY");

for (Integer key : map.keySet()) {
  System.out.println(key + " " + map.get(key));
}

for (Map.Entry<Integer, String> entry : map.entrySet()) {
  System.out.println(entry.getKey() + " " + entry.getValue());
}
val map = mutableMapOf<Int, String>()
map.put(1, "MONDAY")
map.put(2, "TUESDAY")
//    map[1] = "MONDAY"
//    map[2] = "TUESDAY"

mapOf(1 to "MONDAY", 2 to "TUESDAY")

for (key in map.keys) {
    println("$key ${map[key]}")
}

for ((key, value) in map.entries) {
    println("$key $value")
}

Collection의 null 가능성, 자바와 함께 사용하기

  • ? 위치에 따라서 null 가능성 의미가 달라지므로 차이를 잘 이해해야 한다.
    • List<Int?> : 리스트에 null이 들어갈 수 있지만, 리스트는 null 이 아니다.
    • List<Int?>? : 리스트에 null이 들어갈 수 있고, 리스트가 null일 수 있음
    • List<Int>? : 리스트에는 null이 들어갈 수 없지만, 리스트는 null 일 수 있음
  • 자바와 함께 사용시
    • 자바는 읽기 전용 컬렉션과 변견 가능 컬렉션을 구분하지 않는다.
    • 자바는 nullable 타입과 non-nullable 타입을 구분하지 않는다.
  • 따라서 코틀린 쪽의 컬렉션이 자바에서 호출되면 컬렉션 내용이 변할 수 있음을 감안해야 한다.
    • 필자가 자바에서 코틀린 리스트를 사용했을 때는 불변일시에는 값 추가는 안되었다.
  • 또는 코틀린 쪽에서 Collections.unmodifableXXX() 를 활용하면 변경 자체를 막을 수 있다.
class ListWithJava() {
    val list = listOf(100, 200)
    val unmodifiableList = Collections.unmodifiableList(list)
    val modifiableList = mutableListOf(100, 200)
}
ListWithJava listWithJava = new ListWithJava();
List<Integer> unmodifiableList = listWithJava.getUnmodifiableList();
unmodifiableList.add(300);

for (Integer integer : unmodifiableList) {
  System.out.println("integer = " + integer);
}
  • 또한 코틀린에서 자바 컬렉션을 가져다 사용할 때는 플랫폼 타입을 신경써야 한다.
    • 자바에서 List<Integer>를 줄 때, 코틀린에서는 List<Int?>, List<Int?>?, List<Int>? 인지 모른다.
    • 따라서 자바 코드를 보며 맥락을 확인해야 하고, 자바 코드를 가져오는 지점을 wrapping 해야한다.