[HeadFirst] Kotlin collections ::Map

timothy jeong·2021년 11월 1일
0

코틀린

목록 보기
14/20

Map 은 key 와 value 로 구성된 자료 구조이다. 중복되는 value 는 가질 수 있지만, 중복된 key 를 가질 수는 없다.

Map 활용


class Recipe (val title: String) {}

fun main() {
    val myMap: Map<String, Recipe> = mapOf("R1" to Recipe("Soup"), "R2" to Recipe("Steak"))
    
    
    println(myMap.containsKey("R1")) // true
    println(myMap.containsValue(Recipe("Soup"))) // false
    
    println(myMap.get("R1")?.title) // Soup
    println(myMap.get("R3")?.title) // null
    println(myMap.getValue("R1").title) // soup
    println(myMap.getValue("R3").title) // NoSuchElementException
}

map 생성

map 을 초기화 하기 위해서는 key, value pair 를 넣어줘야한다. 이때 key 값은 to 를 이용해서 어떤 value 의 key 가 되는지 명시되어야 한다.

특정값 보유 여부

containsKey() 와 containsValue() 를 이용해서 해당 값을 가지고 있는지 확인 할 수 있다. myMap.containsValue(Recipe("Soup")) 의 결과가 false 인 것은 통해 containsValue() 역시 내부적으로 ===, equals() 를 이용하고 있을 것이라고 짐작할 수 있다.

class Recipe (val title: String) {
    override fun hashCode(): Int {
        return title.hashCode()
    }

    override fun equals(other: Any?): Boolean {
        return this.hashCode() == other.hashCode()
    }
}

Recipe class 에 hashCode() 와 equals() 를 위와 같이 override 하고 다시 myMap.containsValue(Recipe("Soup")) 를 돌려보면 그 결과가 true 로 나오게 된다. title 이 똑같으면 참조값이 어떻든 equivalent 하다고 인식하게 만든 것이다.

get, getValue

get() 함수는 주어진 key 값을 찾을 수 없다면 Null 을 반환하는 반면, getValue() 함수는 예외(Exception)를 발생시킨다.

loop

List, Set 과는 다르게 Map 이 저장하는 값은 Key, Value Pair 이다. 똑같은 방법으로 loop 를 돌리면 돌아가긴 하지만, 아마도 우리가 원하는 값을 보여주진 않을 것이다. {key}={valueInfo} 이런 정보가 출력되기 때문이다.

    for (item in myMap) {
        println(item)
    } 
    //R1=Recipe@276a17
    //R2=Recipe@4c7e12e
    
    for ((key, value) in myMap) {
        println("$key=$value")
    }    

대신 두번째 for문 처럼 구성하면 key 와 value 각각에 접근이 가능하다.

MuatableMap

mutableMap 을 만드는 방법은 예상 되듯이 mutableMapOf() 함수를 사용하는 것이다.

put 을 이용해서 내용물을 추가할 수 있고 또한 assignment 를 이용해서 내용물을 추가할 수 있다. IntelliJ 는 assignment 방법을 추천하는 것 같다. 이는 Java 로 치면 set 을 이용해서 map 의 내용물을 바꾸는 거라는데, 무슨 차이가 있는 걸까? kotlin 문서 에서는 put 도 쓰고있고 굳이 assignment 방식으로 전환해야 하는 이유를 말하고 있지 않다.

remove() 를 이용해서 내용물을 제거할 수 있다.

원래는 put 을 이용해서 map 에 내용물을 추가할 수 있지만, 이를 assignment 방식으로 바꾸는 것이 추천되고 있다.

fun main() {
    val myMap: Map<String, Recipe> = mapOf("R1" to Recipe("Soup"), "R2" to Recipe("Steak"))
    val mutableMap = mutableMapOf("R3" to Recipe("Kimchi"), "R4" to Recipe("SoonDae"))

    mutableMap.put("R5", Recipe("Ramen"))
    mutableMap["R6"] = Recipe("Galbijjim") // recommended...but Why?

    mutableMap.putAll(myMap)
    mutableMap.remove("R4")
    mutableMap.remove("R3", Recipe("Kimchi"))
    mutableMap.clear()
}

Map copy

역시 Map 도 copy 를 만들 수 있다. 특이하게도 List 로도 바꿀 수도 있는데, 그 결과물은 이런 식이다. 각 map 의 key, value pair 를 하나의 원소로 가지고 있는 리스트가 만들어진다.

(R5, Recipe@4b14527)
(R6, Recipe@79a2245d)
(R1, Recipe@276a17)
(R2, Recipe@4c7e12e)

또한 entries 라는 프로퍼티를 이용하면 map 의 내용물을 set 으로 반환 받을 수 있는데, mutableMap 에서 쓰면 mutableSet, 반대는 그냥 set이 반환된다. 또한 keys 프로퍼티를 이용해서 key 의 sey을, values 를 이용해서 value 의 Generic Collection을 얻을 수 있다. 반환받은 자료 구조가 이 mutable 인지 아닌지는 위와 같이 map 의 성격에 따라 결정된다.

keys , values 프로퍼티는 copy 가 아니라 실제 map 을 구성하는 요소이다. 만약 mutable map 에서 가져온 프로퍼티라면, 이를 변경함으로써 map을 업데이트 할 수 있다.

Collection 에 대한 질문

왜 immutable 이 따로 있을까, 다 mutable 이면 안되나?

immutable, mutable 을 나눔으로써 업데이트 되면 안되는 collection 이 다른 코드에서 업데이트 되는 실수를 방지할 수 있다고 한다.

mutable 을 쓰고 있지만 다른 영역에서는 immutable 로 나타내야한다면?


val x = mutableSet(1, 2,)
val y: Set<Int> = x 

이렇게 하고 y만 보여준다면, 동일한 내용물을 업데이트 할 수 없는 버전으로 보여줄 수 있다.

profile
개발자

0개의 댓글