map 是一種特殊的數(shù)據(jù)結(jié)構(gòu):一種元素對(duì)(pair)的無(wú)序集合歉摧,pair 的一個(gè)元素是key,對(duì)應(yīng)的另一個(gè)元素是value,所以這個(gè)結(jié)構(gòu)也稱為關(guān)聯(lián)數(shù)組或字典叁温。
map的讀取和設(shè)置也類似slice一樣再悼,通過(guò)key來(lái)操作,只是slice的index只能是int類型膝但,而map多了很多類型冲九,可以是int,可以是string及所有完全定義了==與!=操作的類型跟束。
初始化
map 是引用類型莺奸,可以使用如下聲明:
make(map[KeyType]ValueType, initialCapacity)
make(map[KeyType]ValueType)
map[KeyType]ValueType{}
map[KeyType]ValueType{key1 : value1, key2 : value2, ... , keyN : valueN}
//聲明一個(gè)key是字符串,值為int的字典冀宴,這種方式的聲明需要在使用之前使用make初始化
var numbers map[string]int
numbers = make(map[string]int)
numbers["one"] = 1
numbers["two"] = 2
numbers["three"] = 3
未初始化的 map 的值是 nil灭贷。
func test1() {
? ? map1 := make(map[string]string, 5)? // 指定初始容量
? ? map2 := make(map[string]string)
? ? map3 := map[string]string{}
? ? map4 := map[string]string{"a": "1", "b": "2", "c": "3"}
? ? fmt.Println(map1, map2, map3, map4) // map[] map[] map[] map[a:1 b:2 c:3]
}
以上示例代碼用4種方式分別創(chuàng)建map,其中第一種和第二種的區(qū)別在于花鹅,有沒(méi)有指定初始容量,不過(guò)使用的時(shí)候則無(wú)需在意這些枫浙,因?yàn)閙ap的本質(zhì)決定了刨肃,一旦容量不夠,它會(huì)自動(dòng)擴(kuò)容箩帚。
注意:必須要先初始化才能給map賦值設(shè)置元素真友,不然會(huì)引起 panic: assign to entry in nil map。
func main(){
? ? ages01 := map[string]int{
? ? ? ? "alice":31,
? ? ? ? "bob":13,
? ? }
? ? ages02 := make(map[string]int)
? ? ages02["chris"] =20 ? ?// 通過(guò)==進(jìn)行map賦值
? ? ages02["paul"] = 30
? ? //age01和age02兩種初始化的方式等價(jià)
? ? m1 := make(map[string]int)
? ? m2 := map[string]int{}
? ? //m1和m2創(chuàng)建方式等價(jià)紧帕,都是創(chuàng)建了一個(gè)空的的map盔然,這個(gè)時(shí)候m1和m2沒(méi)有任何元素
? ? for name,age := range ages01{
? ? ? ? fmt.Printf("%s\t%d\n",name,age) ? ?// bob 13, alice 31
? ? }
? ? for name,age := range ages02{
? ? ? ? fmt.Printf("%s\t%d\n",name,age) ? ?// chris 20, paul 30
? ? }
? ? var null_map map[string]int? ? //聲明但未初始化map,此時(shí)是map的零值狀態(tài)(只有一個(gè)nil元素)
? ? empty_map := map[string]int{}? //創(chuàng)建了初始化了一個(gè)空的的map是嗜,這個(gè)時(shí)候empty_map沒(méi)有任何元素
? ? fmt.Println(m1 != nil && m2 != nil) //true
? ? fmt.Println(len(null_map)==0) ? ?// true
? ? fmt.Println(null_map ==nil)? ? //true,此時(shí)是map的零值狀態(tài)(nil)
? ? fmt.Println(len(empty_map)==0) ? ?// true
? ? fmt.Println(empty_map ==nil)? ? //false,空的的map不等價(jià)于nil(map的零值狀態(tài))
? ? empty_map["test"] = 12? ? ? ? ? //執(zhí)行正常愈案,空的的map可以賦值設(shè)置元素
? ? null_map["test"] = 12? ? ? ? ? ? //panic: assignment to entry in nil map,無(wú)法給未初始化的map賦值設(shè)置元素
}
需要注意的幾點(diǎn)
1)map是無(wú)序的鹅搪,每次打印出來(lái)的map都會(huì)不一樣站绪,它不能通過(guò)index獲取,而必須通過(guò)key獲壤鍪痢恢准;
2)map的長(zhǎng)度是不固定的,也就是和slice一樣甫题,也是一種引用類型馁筐;
3)內(nèi)置的len函數(shù)同樣適用于map,返回map擁有的key的數(shù)量坠非;
4)map的值可以很方便的修改敏沉,通過(guò)numbers["one"]=11可以很容易的把key為one的字典值該為11;
5)map和其他基本類型不同,它不是thread-safe赦抖,在多個(gè)go-routine存取時(shí)舱卡,必須使用mutex lock機(jī)制;
map元素的同步更改
m := make(map[string]string)
m["hello"] = "Hello"
m1 := m
m1["hello"] = "World"
map是一種引用類型队萤,如果兩個(gè)map同時(shí)指向一個(gè)底層轮锥,那么一個(gè)改變,另一個(gè)也相應(yīng)的改變要尔。
map中元素是否存在
rating := map[string]float32 {"c":5, "Go":4.5, "Python":5.2, "PHP":2.4}
csharpRating, ok := rating["c#"]
if ok {
? ? // 存在
} else {
? ? // 不存在
}
if _, ok := map[key]; ok {?
//如果存在則執(zhí)行?
}
map元素遍歷
range for 可用于遍歷map 中所有的元素舍杜,不過(guò)需要注意因?yàn)?map本身是無(wú)序的,因此對(duì)于程序的每次執(zhí)行赵辕,不能保證使用 range for 遍歷 map的順序總是一致的既绩。例如:
package main
import (?
? ? "fmt"
)
func main() {?
? ? personSalary := map[string]int{
? ? ? ? "steve": 12000,
? ? ? ? "jamie": 15000,
? ? }
? ? personSalary["mike"] = 9000
? ? for key, value := range personSalary {
? ? ? ? fmt.Printf("personSalary[%s] = %d\n", key, value)
? ? }
}
獲取map中所有的key
sizes := map[string]int{"XL": 20,"L":? 10,"M":? 5}
// Loop over map and append keys to empty slice.
keys := []string{}
for key, _ := range sizes {
? ? keys = append(keys, key)
}
// This is a slice of the keys.
fmt.Println(keys)
map元素增刪改查
首先這里map元素的增加和修改元素的語(yǔ)法一致,只需要map[K]=V即可还惠。例如:
package main
import (
? ? "fmt"
)
func main() {
? ? personSalary := make(map[string]int)
? ? personSalary["steve"] = 12000? ? //增加元素
? ? personSalary["jamie"] = 15000? ? //增加元素
? ? personSalary["mike"] = 9000? ? ? //增加元素
? ? fmt.Println("map before change", personSalary)
? ? personSalary["mike"] = 10000? ? //修改元素
? ? fmt.Println("map after change", personSalary)
}
輸出:
map before change map[steve:12000 jamie:15000 mike:9000]
map after change map[steve:12000 jamie:15000 mike:10000]
刪除元素需要使用內(nèi)置函數(shù) delete饲握,該函數(shù)根據(jù)鍵來(lái)刪除一個(gè)元素。需要強(qiáng)調(diào)delete函數(shù)沒(méi)有返回值蚕键,例如:
package main
import (?
? ? "fmt"
)
func main() {?
? ? personSalary := map[string]int{
? ? ? ? "steve": 12000,
? ? ? ? "jamie": 15000,
? ? }
? ? personSalary["mike"] = 9000
? ? fmt.Println("map before deletion", personSalary)
? ? delete(personSalary, "steve")
? ? fmt.Println("map after deletion", personSalary)
}
輸出:
map before deletion map[steve:12000 jamie:15000 mike:9000]?
map after deletion map[mike:9000 jamie:15000]
map作為函數(shù)參數(shù)
func main() {
? ? colors := map[string]int {
? ? ? ? "blue":? 10,
? ? ? ? "green": 20,
? ? }
? ? PrintGreen(colors)
}
func PrintGreen(colors map[string]int) {
? ? fmt.Println(colors["green"])
}
按key進(jìn)行排序
import (
? ? "fmt"
? ? "sort"
)
func main() {
? ? m := make(map[int]string)
? ? m[1] = "b"
? ? m[2] = "c"
? ? m[0] = "a"
? ? var keys []int
? ? for k := range m {
? ? ? ? keys = append(keys, k)
? ? }
? ? sort.Ints(keys) ? ?// 排序:升序
? ? for _, k := range keys {
? ? ? ? fmt.Println("Key:", k, "Value:", m[k])
? ? }
}
map的比較
Go 語(yǔ)言中map和slice救欧,func一樣,不支持 == 操作符锣光,就是不能直接比較笆怠。唯一合法的就是和nil作比較,判斷該map是不是零值狀態(tài)誊爹。
如果想自定義一個(gè)函數(shù)蹬刷,來(lái)比較兩個(gè)map是否相等,就可以遍歷比較它們的鍵和值是否完全相等频丘,代碼如下:
? ? func map_equal(x,y map[string]string) bool {
? ? ? if len(x) != len(y) {
? ? ? ? return false
? ? ? }
? ? ? for k,xv :=range x {
? ? ? ? if yv,ok := y[k]; !ok || yv != xv {
? ? ? ? ? ? ? ? ? return false
? ? ? ? ? ? ? ? }
? ? ? }
? ? ? return true
? ? }