概要
今回のブログポストでは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
なので、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
を割り当てたことか、デフォルトが出力されたか分かりません。
ここでマップの2つのリターン値(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で開発されています。興味がある方はアプリをダウンロードしてアプリを使ってくれると本当に助かります。