포스트

Kotlin의 배열과 컬렉션 다루기


배열

  • 배열은 잘 사용되지 않는다.
    • List사용을 권장함


Java의 배열 사용

1
2
3
4
5
    int[] array = {100, 200};

    for(int i=0; i<array.length; i++) {
      System.out.println("순번 : "+ i + " / 값 : " + array[i]);
    }


Kotlin의 배열 사용

1
2
3
4
5
6
7
8
9
10
11
    val array = arrayOf(100, 200)

// 0부터 마지막 index 까지 반복
for (i in array.indices){
  println("순번 : ${i} / 값 : ${array[i]}")
}

// withIndex를 활용해 index 와 value 를 한번에 가져올 수 있다.
for ((index, value) in array.withIndex()){
  println("순번 : ${index} / 값 : ${value}")
}




Kotlin의 컬렉션 - List, Set, Map

  • 불변, 가변을 꼭 지정해 주어야 한다.


Kotlin의 계층 구조

img.png


  • 가변(mutable) 클래스 : element를 삭제, 추가 가능
  • 불변 클래스 : element를 삭제, 추가 불가
    • Java로 예를 들면 -> Collections.unmodifiableList()를 붙인 것과 같다.
    • 불변이라도 reference TypeElement필드는 수정 가능
    • ex). 원래 존재하는 값을 수정을 할 수 있다.
      • 단, 값을 추가, 삭제는 불가능

Kotlin은 불변/가변을 지정해 주어야 한다는 사실을 꼭 기억해야 한다.



List

Java의 List

1
final List<Integer> numbers = Arrays.asList(100, 200);


Kotlin의 List

1
2
3
4
5
    // List
    val numbers = listOf(100, 200)

    // 비어 있는 리스트의 타입은 명시적으로 적어야 한다.
    val emptyList = emptyList<Int>()
  • listOf()를 통해 불변 리스트를 만든다.
  • emptyList<Type>()를 통해 비어 있는 리스트를 만든다.


1
2
3
4
5
fun main() {
    printNumbers(emptyList())
}

private fun printNumbers(numbers: List<Int>){}
  • 위와 같이 Type을 추론할 수 있다면 타입을 생략할 수 있다.


Java의 for문

1
2
3
4
5
6
7
8
9
10
11
12
// 하나 가져오기
System.out.println(numbers.get(0));

// For Each
for(int number : numbers){
    System.out.println(number);
  }

// 전통적인 For문
for(int i = 0; i < numbers.size(); i++){
    System.out.printf("%s %s", i, numbers.get(i));
  }


Kotlin의 for문

1
2
3
4
5
6
7
8
9
10
11
12
    // 배열처럼 대괄호로 가져올 수 있다.
    println(numbers[0])

    // for each문
    for (number in numbers){
        println(number)
    }

    // for withIndex() -> key, value 를 같이 가져올 수 있다.
    for ((idx, value) in numbers.withIndex()) {
        println("${idx} ${value}")
    }


가변(Mutable) 리스트를 만들고 싶다면?

1
2
3
    // 가변 List
    val numbersV2 = mutableListOf(400, 600)
    numbersV2.add(300)

우선 불변 리스트로 만들고 필요한 경우만 가변 리스트로 바꾸자!!




Set (집합)

  • 집합은 List와 다르게 순서가 없고 같은 element는 하나만 존재 가능
  • 자료구조적 의미만 제외하면 모든 기능이 List와 비슷하다.


Kotlin의 Set

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Set
    val setNumbers = setOf(100, 200)

    // for each
    for(number in setNumbers) {
        println(number)
    }

    // for withIndex()
    for ((index, value) in setNumbers.withIndex()) {
        println("$index $value")
    }

    // Set의 가변(Mutable)
    // 기본 구현체는 LinkedHashSet
    val setNumbersV2 = mutableSetOf(400, 600)




Map


Java의 Map

1
2
3
4
5
6
7
    // JDK 8까지
    Map<Integer, String> map = new HashMap<>();
    map.put(1, "MONDAY");
    map.put(2, "TUESDAY");

    // JDK 9부터
    Map.of(3, "WEDNESDAY", 4, "THURSDAY");


Kotlin의 Map

1
2
3
4
5
6
    val oldMap = mutableMapOf<Int, String>()
    oldMap[1] = "MONDAY"
    oldMap[2] = "TUESDAY"

    // mapOf 를 사용하는 경우는 key, value 사이에 to 를 넣어야 한다.
    mapOf(3 to "WEDNESDAY", 4 to "THURSDAY")
  • Kotlin도 동일하게 MutableMap을 만들어 넣을 수도 있고, 정적 팩토리 메소드를 바로 활용할 수도 있다.
  • mutableMapOf에서 Type을 추론할 수 있도록 Type을 지정
    • 가변 Map이기 때문에 Java와 같이 put을 사용해도 되고, map[key] = value를 사용해도 된다.
    • mapOf를 사용해서 key to value를 사용해서 불변 map을 만들수 있다.


Map의 반복문


Java에서 Map 반복문

1
2
3
4
5
6
7
8
9
10
11
    // Key를 가져와서 반복하는 방법
    for(int key : map.keySet()){
      System.out.println(key);
      System.out.println(map.get(key));
    }
    
    // Entry라는 Key와 Value가 함께들어 있는 객체를 반복하는 방법
    for(Map.Entry<Integer, String> entry : map.entrySet()){
      System.out.println(entry.getKey());
      System.out.println(entry.getValue());
    }


Kotlin에서 Map 반복문

1
2
3
4
5
6
7
8
9
10
11
    // Key를 가져와서 반복하는 방법
    for (key in oldMap.keys) {
        println(key)
        println(oldMap[key])
    }

    // entries라는 프로퍼티스를 사용해서 Key와 Value를 사용하는 방법
    for ((key, value) in oldMap.entries) {
        println(key)
        println(value)
    }




컬렉션의 null 가능성 / Java와 함께 사용


?의 위치에 따라서 미묘하게 null 가능성이 바뀐다.

  • List : `List`에 `null`이 들어갈 수 있지만, `List`는 절대 `null`이 아님

  • List? : `List`에는 `null`이 들어갈 수 없지만, `List`는 `null`일 수 있음

  • List<Int?>? : Listnull이 들어갈 수도 있고, Listnull일 수도 있음


Java는 읽기 전용 컬렉션과 변경 가능 컬렉션을 구분하지 않는다.

  • Kotlin의 불변 리스트를 Java에서 가져오는 경우, Java에서는 불변인지 모르기 때문에 Element를 추가할 수 있다.
  • 이후에 Kotlin으로 돌려주면 불변 리스트에 Element가 추가되어 오동작 할수 있다.


Java는 nullable 타입과 non-nullable 타입을 구분하지 않는다.

  • Kotlinnon-nullable 리스트를 Java에서 가져오는 경우 Java에서는 non-nullable를 모르기 때문에 null을 추가할 수 있다.
  • 이후에 Kotlin으로 돌려주면 non-nullablenull이 추가되어 오동작 할수 있다.


결국 Kotlin 쪽의 컬렉션이 Java에서 호출되면 컬렉션 내용이 변할 수 있음을 감안해야 한다.

  • Kotlin쪽에서 Collections.unmodifableXXX()를 활용하면 변경 자체를 막을 수 있다.


Kotlin에서 Java 컬렉션을 가져다 사용할 때는 플랫폼 타입을 신경써야 한다.

  • Java에서 List<Integer>Kotlin에 보내면 List<Int?> / List<Int>? / List<Int?>? 인지 알수 없다.
  • Java코드를 보며, 맥락을 확인하고 Java코드를 가져오는 지점을 wrapping한다.
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.