Map

배열이나 슬라이스는 저장된 각 요소에 접근하기 위한 키로 숫자를 사용하는 자료구조이다. 반면 맵은 키로 숫자가 아닌 다양한 타입을 사용할 수 있다.

Python의 dict 및 Ruby의 hash 에 해당한다고 볼 수 있다.

맵 타입

맵 타입은 다음과 같이 정의한다.

MapType     = "map" "[" KeyType "]" ElementType .

“map”이라는 키워드 뒤에 키 타입과 요소 타입을 각각 기술한다. 예를 들어 문자열을 키로 하고 정수를 저장하는 맵은 다음과 같이 쓸 수 있다.

var m map[string]int

생성 및 초기화

맵 역시 다른 타입들과 유사하게 리터럴 초기화나 동적 할당이 가능하다.

리터럴 (literal) 초기화

맵 타입 변수를 리터럴 초기화하기 위해서는 키와 값을 colon(:)으로 구분해 중괄호({}) 안에 묶어 쓴다.

rating := map[string]float32 {"Go":5.0, "C/C++":4.5, "Python":4.0}

동적 할당

맵을 동적으로 할당하기 위해서는 슬라이스와 동일하게 make() 함수를 사용한다.

make()를 이용해 슬라이스를 만들 때는 슬라이스의 길이(len) 및 용량(cap)을 입력 인수로 추가할 수 있었지만, 맵의 경우에는 용량만 입력할 수 있다.

rating := make(map[string]float32)
rating := make(map[string]float32, 100)

접근/추가/삭제

접근/추가

맵에 포함된 요소에 접근하기 위해서는 배열이나 슬라이스처럼 대괄호([]) 안에 키를 입력한다.

rating["Go"] = 4.9
go_point := rating["Go"]

맵을 사용할 때 한 가지 주의할 점은 존재하지 않는 키를 이용해 접근하는 경우이다. 배열이나 슬라이스의 경우를 먼저 살펴보자.

// array
a := [3]int {1, 2, 3}
a[3] = 4

위와 같이 존재하지 않는 키—범위를 벗어난 키—를 사용하는 경우 다음과 같은 오류가 발생한다.

invalid array index 3 (out of bounds for 3-element array)

위 예를 그대로 슬라이스로 바꿔보자.

// slice
s := []int {1, 2, 3}
s[3] = 4

슬라이스는 크기가 정해진 타입이 아니므로 배열처럼 컴파일 오류가 발생하지는 않지만 다음과 같이 runtime error가 발생한다.

panic: runtime error: index out of range

반면 맵의 경우는 존배하지 않는 키에 값을 입력하는 경우 요소를 추가한다.

// map
m := map[string]int {"A":1, "B":2, "C":3}
m["D"] = 4
fmt.Println(m["D"])
// 4

즉, 맵에 요소를 추가할 때는 새로운 키에 값을 입력하기만 하면 된다. append()를 이용해 요소를 추가하는 슬라이스에 비해 간단하다.

존재하지 않는 키를 이용해 요소값을 읽는 경우를 살펴보자. 주의할 점은 여기에 있다. 이 경우 배열이나 슬라이스는 위와 동일하게 오류가 발생한다.

m := map[string]int {"A":1, "B":2, "C":3}
fmt.Println(m["D"])

존재하지 않는 키의 요소값을 읽는 경우 맵은 zero value를 반환한다. 즉, 위 예는 0을 출력한다. 문제는 “D”라는 키의 값이 0으로 입력된 것인지, “D”라는 키가 존재하지 않는 것인지 구분할 수 없다는 점이다.

comma-ok 형식

함수로 돌아가 보자. Go 함수의 가장 큰 특징 중 하나가 반환값을 복수개로 지정할 수 있다는 것이다. 이 특징을 이용해서 Go에서는 첫 번째 반환값으로 원래 반환하려고 했던 값을, 두 번째 반환값으로 성공/실패 여부를 반환하는 방식이 많이 사용된다. 이를 줄여서 comma-ok 형식이라고 부른다. 반환값이 두 개 이상인 경우 각 반환값이 comma로 구분되고, 두 번째 반환값이 ok/not ok를 나타내기 때문이다.

요소를 읽을 때 맵은 comma-ok 형식으로 값을 반환한다. 키의 존재 여부는 두 번째 반환값을 통해 확인할 수 있다.

m := map[string]int {"A":0, "B":1}
c, ok := m["C"]
if ok {
    fmt.Println(c)
} else {
    fmt.Println("not exist")
}

삭제

맵의 요소를 삭제할 때는 delete() 함수를 사용한다.

func delete(m map[Type]Type1, key Type)