개요
이번 블로그 포스트에서는 Golang에서 맵(Map)
에 대해 알아보고 사용하는 방법에 대해서 알아보도록 하겠습니다. 이 블로그 포스트에서 소개하는 코드는 다음 링크를 통해 확인하실 수 있습니다.
맵(Map)
맵(Map)은 키와 값 형태로 데이터를 저장하는 자료 구조입니다. 프로그래밍 언어에 따라 딕셔너리(Dictionary), 해시테이블(Hash table), 해시맵(Hash map)등으로 부르기도 합니다.
Golang에서는 다음과 같이 맵을 정의할 수 있습니다.
// map[KEY_TYPE]VALUE_TYPE
map[string]int
make
함수를 통해 변수를 선언할 수 있습니다.
m := make(map[string]string)
이를 확인하기 위해, main.go
파일을 생성하고 다음과 같이 수정합니다.
package main
import "fmt"
func main() {
m := make(map[string]string)
m["name"] = "John"
m["country"] = "Korea"
fmt.Println(m)
m["city"] = "Seoul"
fmt.Println(m)
fmt.Printf("No key: %s / %T\n", m["language"], m["language"])
}
이를 실행하면 다음과 같은 결과가 출력됩니다.
# go run main.go
map[country:Korea name:John]
map[city:Seoul country:Korea name:John]
No key: / string
맵에 존재하지 않는 키에 접근하면, 값의 타입의 기본값이 반환됩니다. 위에 예제에서는 값의 타입인 string
의 기본값인 ``이 반환된 것을 확인할 수 있습니다.
키값의 존재 여부
맵에 존재하지 않는 키에 접근하면, 값의 타입의 기본값이 반환되는 것을 알 수 있었습니다. 그럼 다음과 같이 값의 기본값을 저장한 경우, 이것이 값이 할당되지 않아 기본값이 반환되는지, 기본값을 할당한 것인지 어떻게 알 수 있을까요?
package main
import "fmt"
func main() {
m := make(map[string]string)
m["name"] = ""
fmt.Printf("name: %s / %T\n", m["name"], m["name"])
fmt.Printf("country: %s / %T\n", m["country"], m["country"])
}
이를 위해, Golang에서는 키를 사용하여 맵의 값을 가져올 때, 해당 키값이 맵에 존재하는지 여부를 함께 반환해 줍니다.
v, ok := m["name"]
해당 키값이 맵에 존재하는 경우 ok
변수는 true
가 되고, 존재하지 않는 경우 false
가 됩니다.
이를 확인하기 위해 main.go
파일을 다음과 같이 수정합니다.
package main
import "fmt"
func main() {
m := make(map[string]string)
m["name"] = ""
fmt.Printf("name: %s / %T\n", m["name"], m["name"])
fmt.Printf("country: %s / %T\n", m["country"], m["country"])
v, ok := m["name"]
fmt.Println(v, ok)
v, ok = m["country"]
fmt.Println(v, ok)
}
이를 실행하면 다음과 같은 결과가 출력됩니다.
# go run main.go
name: / string
country: / string
true
false
이것으로 우리는 해당 키값이 존재하는지 여부를 확인할 수 있습니다.
요소 삭제
Golang에서는 다음과 같이 delete
함수를 사용하여 맵의 요소를 삭제할 수 있습니다.
delete(MAP_VARIABLE, KEY)
이를 확인하기 위해 main.go
파일을 다음과 같이 수정합니다.
package main
import "fmt"
func main() {
m := make(map[int]int)
m[1] = 0
m[2] = 2
m[3] = 3
v, ok := m[1]
fmt.Println(v, ok)
delete(m, 1)
v, ok = m[1]
fmt.Println(v, ok)
}
이를 실행하면 다음과 같은 결과를 얻을 수 있습니다.
# go run main.go
0 true
0 false
delete
함수를 사용하여 해당 키의 요소를 제거하기 전에도 값이 0
이며, delete
함수를 사용하여 해당 요소를 제거하였을 때에도 0
이 반환된 것을 확인할 수 있습니다.
delete
함수를 사용하여 해당 요소를 제거한 후에 0
이 반환된 것은 맵의 해당 키값이 존재하지 않아 값 타입의 기본값(int)인 0
이 반환된 것입니다. 이렇게 값만을 확인하면 0
을 대입한 것인지, 기본값이 출련된 것인지 알 수 없습니다.
여기서 맵의 두번째 반환값(ok
)을 체크하여 해당 값이 설정된 값인지, 제거되고 기본값이 반환되는 것인지 확인할 수 있습니다.
맵 순회
맵의 모든 키/값에 접근하기 위해서 다음과 같이 for
문과 range
를 사용할 수 있습니다.
m := make(map[int]int)
for key, value := range m {
fmt.Println(key, value)
}
이를 확인하기 위해 main.go
파일을 다음과 같이 수정합니다.
package main
import "fmt"
type Product struct {
Name string
Price int
}
func main() {
m := make(map[int]Product)
m[19] = Product{Name: "TV", Price: 3000}
m[16] = Product{Name: "Phone", Price: 1000}
m[18] = Product{Name: "PC", Price: 500}
m[17] = Product{Name: "Tablet", Price: 2000}
for key, value := range m {
fmt.Println(key, value)
}
}
이를 실행하면 다음과 같은 결과를 얻을 수 있습니다.
# go run main.go
18 {PC 500}
17 {Tablet 2000}
19 {TV 3000}
16 {Phone 1000}
Golang은 해시맵(Hash map)을 사용하고 있기 때문에, 맵의 순서가 보장되지 않습니다.
- Hash map: unsorted map
- Sorted map: sorted map
맵, 배열, 리스트 비교
맵과 배열, 리스트를 Big-O
표기법을 사용하여 비교하면 다음과 같습니다.
기능 | 배열, 슬라이스 | 리스트 | 맵 |
---|---|---|---|
추가 | O(N) | O(1) | O(1) |
삭제 | O(N) | O(1) | O(1) |
읽기 | O(1) | O(N) | O(1) |
맵은 추가, 삭제, 읽기가 모두 O(1)
으로 성능이 좋지만, for
문을 사용하여 순회할 때 순서가 보장이 안되며, 많은 메모리를 차지한다는 단점이 있습니다.
Big-O 표기법에 관해 궁금하신 분들은, 다음 링크를 통해 이전 블로그 포스트를 확인해 주시기 바랍니다.
완료
이것으로 Golang에서 맵을 사용하는 방법에 대해서 알아보았습니다. Golang에서 사용되는 다른 자료 구조들인 리스트, 큐, 스택, 링이 궁금하신 분들은 이전 블로그 포스트를 참고하시기 바랍니다.
제 블로그가 도움이 되셨나요? 하단의 댓글을 달아주시면 저에게 큰 힘이 됩니다!
앱 홍보
Deku
가 개발한 앱을 한번 사용해보세요.Deku
가 개발한 앱은 Flutter로 개발되었습니다.관심있으신 분들은 앱을 다운로드하여 사용해 주시면 정말 감사하겠습니다.