map은 순서없는 key-value 쌍의 집합체이다. associative array 라고도 알려져 있으며, 해시 테이블 또는 딕셔너리라고도 표현되기도 한다. 연관 키를 사용하여 값을 찾을 때 사용된다. 아래의 예를 보자.
1 | var x map[string]int | cs |
map 타입은 map 이라는 키워드 다음에 키의 타입이 괄호 안에 표시되고, 뒤이어 값의 타입이 표시된다. 위의 예시같은 경우 다음과 같이 읽으면 된다.
“x is a map of strings to ints.”
배열이나 슬라이스와 마찬가지로 maps 은 괄호롤 사용해접근이 가능하다. 다음을 실행시켜보자.
1 2 3 4 5 6 7 8 9 | package main import "fmt" func main() { var x map[string]int x["key"] = 10 fmt.Println(x) } | cs |
실횅하면 다음과 같은 에러를 볼 수 있을 것이다.
1 2 3 4 5 6 7 8 | ~/go/src/golang-book/chapter6# go run main.go panic: assignment to entry in nil map goroutine 1 [running]: main.main() /root/go/src/golang-book/chapter6/main.go:7 +0x4f exit status 2 | cs |
지금까지는 컴파일 에러만 봤지만, 이번 건은 런타임 에러다. 이름에서 짐작할 수 있듯이, 컴파일 에러가 컴파일 당시 발생하듯, 런타임 에러는 프로그램을 실행할 때 발생한다.
문제는 맵은 사용되기전 먼저 초기화되어야한다는 점이다. 다음과 같이 작성해야 한다.
1 2 3 4 5 6 7 8 9 10 | package main import "fmt" func main() { x := make(map[string] int) x["key"] = 10 fmt.Println(x) } | cs |
1 2 3 | ~/go/src/golang-book/chapter6# go run main.go map[key:10] | cs |
int 타입의 키를 이용해 맵을 생성할 수도 있다.
1 2 3 4 5 6 7 8 9 10 | package main import "fmt" func main() { x := make(map[int]int) x[1] = 10 fmt.Println(x[1]) } | cs |
1 2 3 | :~/go/src/golang-book/chapter6# go run main.go 10 | cs |
배열과 유사해보이지만, 몇 가지에서 차이점을 나타낸다.
아이템을 추가할 때마다 맵의 길이가 달라진다는 점이다. 처음 생성되었을 때는 0 의 길이를 갖는데, x[1] = 10 이후에는 1의 길이를 가지게 된다. 둘째, 맵은 순차적이지 않다. 배열이 x[1] 이라면 x[0] 이 존재한다고 말할 수 있지만 맵에 대해서는 반드시 그렇다고 말할 수 없다.
내장된 삭제 함수를 이용해서 맵에서 아이템을 삭제할 수 있다.
1 | delete(x, 1) | cs |
맵을 사용한 예제 프로그램을 살펴보자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | package main import "fmt" func main() { elements := make(map[string]string) elements["H"] = "Hydrogen" elements["He"] = "Helium" elements["Li"] = "Lithium" elements["Be"] = "Beryllium" elements["B"] = "Boron" elements["C"] = "Carbon" elements["N"] = "Nitrogen" elements["O"] = "Oxygen" elements["F"] = "Fluorine" elements["Ne"] = "Neon" fmt.Println(elements["Li"]) } | cs |
1 2 3 | ~/go/src/golang-book/chapter6# go run main.go Lithium | cs |
이상은 심볼로 인덱스된 10개의 화학 원소를 나타내는 맵이다. lookup 테이블이나 딕셔너리처럼 맵을 사용하는 전형적인 예이다. 만약 존재하지 않는 원소를 다음과 같이 검색하면 어떻게 될까?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | package main import "fmt" func main() { elements := make(map[string]string) elements["H"] = "Hydrogen" elements["He"] = "Helium" elements["Li"] = "Lithium" elements["Be"] = "Beryllium" elements["B"] = "Boron" elements["C"] = "Carbon" elements["N"] = "Nitrogen" elements["O"] = "Oxygen" elements["F"] = "Fluorine" elements["Ne"] = "Neon" fmt.Println(elements["Un"]) } | cs |
아무 것도 반환되지 않음을 볼 수 있다.
1 2 3 | ~/go/src/golang-book/chapter6# go run main.go ~/go/src/golang-book/chapter6# | cs |
기술적으로 맵은 비어있는 값에 대해서는 제로 값을 반환한다. 다음과 같은 조건문(elements["Un"] == "")으로 비어있는지 체크를 할 수도 있지만, Go 는 좀 더 나은 방법을 제공한다.
1 2 | name, ok := elements["Un"] fmt.Println(name, ok) | cs |
맵의 원소에 접근하는 행위를 통해 2개의 값을 반환하게 된다. 첫 번째 값은 lookup 의 결과이고, 두번째는 lookup 이 성공적이었는지 알려준다. Go 에서는 다음과 같은 코드를 종종 보게 된다.
1 2 3 | if name, ok := elements["Un"]; ok { fmt.Println(name, ok) } | cs |
먼저 맵에서 값을 찾도록 한다. 그리고 그것이 성공적인 경우에만 블록안의 코드를 실행시키도록 하는 것이다.
배열에서 이미 보았듯이 맵을 생성하는 좀 더 간략한 방법이 존재한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | package main import "fmt" func main() { elements := map[string]string{ "H": "Hydrogen", "He": "Helium", "Li": "Lithium", "Be": "Beryllium", "B": "Boron", "C": "Carbon", "N": "Nitrogen", "O": "Oxygen", "F": "Fluorine", "Ne": "Neon", } fmt.Println(elements["Ne"]) } | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | package main import "fmt" func main() { elements := map[string]string{ "H": "Hydrogen", "He": "Helium", "Li": "Lithium", "Be": "Beryllium", "B": "Boron", "C": "Carbon", "N": "Nitrogen", "O": "Oxygen", "F": "Fluorine", "Ne": "Neon", } if name, ok := elements["Ne"]; ok { fmt.Println(name, ok) } } | cs |
1 2 3 | ~/go/src/golang-book/chapter6# go run main.go Neon true | cs |
맵은 종종 종합적인 정보를 저장하기 위해 활용할 수 있다. 단순히 원소의 이름 뿐만 아니라 표준 상태까지 저장하는 프로그램을 만들어보자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | package main import "fmt" func main() { elements := map[string]map[string]string{ "H": map[string]string{ "name":"Hydrogen", "state":"gas", }, "He": map[string]string{ "name":"Helium", "state":"gas", }, "Li": map[string]string{ "name":"Lithium", "state":"solid", }, "Be": map[string]string{ "name":"Beryllium", "state":"solid", }, "B": map[string]string{ "name":"Boron", "state":"solid", }, "C": map[string]string{ "name":"Carbon", "state":"solid", }, "N": map[string]string{ "name":"Nitrogen", "state":"gas", }, "O": map[string]string{ "name":"Oxygen", "state":"gas", }, "F": map[string]string{ "name":"Fluorine", "state":"gas", }, "Ne": map[string]string{ "name":"Neon", "state":"gas", }, } if el, ok := elements["Li"]; ok { fmt.Println(el["name"], el["state"]) } } | cs |
실행결과는 다음과 같다.
1 2 3 | ~/go/src/golang-book/chapter6# go run main.go Lithium solid | cs |
맵의 타입이 map[string]string 에서 map[string]map[string]string 으로 변경되었음에 주목하자. 즉, 문자열을 문자열을 문자열로 매칭시키는 맵으로 매칭하는 맵을 가지게 된 셈이다. 바깥쪽 맵은 원소의 기호에 기반한 lookup 테이블처럼 사용되고, 안쪽 맵은 해당 원소에 대한 종합적인 정보를 저장하는데 사용된다.
'프로그래밍 Programming' 카테고리의 다른 글
Go 언어 입문 - Control Structures - Functions (2) (golang-book) (0) | 2018.09.09 |
---|---|
Go 언어 입문 - Control Structures - Functions (1) (golang-book) (0) | 2018.09.09 |
Go 언어 입문 - Control Structures - Slice (golang-book) (0) | 2018.09.04 |
Go 언어 입문 - Control Structures - Array (golang-book) (0) | 2018.09.01 |
Go 언어 입문 - Control Structures - Switch (golang-book) (0) | 2018.08.31 |