Go入門12:映射 map,引用類型

map 是一種特殊的數(shù)據(jù)結(jié)構(gòu):一種元素對(pair)的無序集合,pair 的一個元素是key,對應(yīng)的另一個元素是value,所以這個結(jié)構(gòu)也稱為關(guān)聯(lián)數(shù)組或字典。

map的讀取和設(shè)置也類似slice一樣,通過key來操作,只是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}

//聲明一個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ū)別在于,有沒有指定初始容量,不過使用的時候則無需在意這些,因為map的本質(zhì)決定了,一旦容量不夠,它會自動擴(kuò)容。

注意:必須要先初始化才能給map賦值設(shè)置元素,不然會引起 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 ? ?// 通過==進(jìn)行map賦值

? ? ages02["paul"] = 30

? ? //age01和age02兩種初始化的方式等價

? ? m1 := make(map[string]int)

? ? m2 := map[string]int{}

? ? //m1和m2創(chuàng)建方式等價,都是創(chuàng)建了一個空的的map,這個時候m1和m2沒有任何元素

? ? 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,此時是map的零值狀態(tài)(只有一個nil元素)

? ? empty_map := map[string]int{}? //創(chuàng)建了初始化了一個空的的map,這個時候empty_map沒有任何元素

? ? fmt.Println(m1 != nil && m2 != nil) //true

? ? fmt.Println(len(null_map)==0) ? ?// true

? ? fmt.Println(null_map ==nil)? ? //true,此時是map的零值狀態(tài)(nil)

? ? fmt.Println(len(empty_map)==0) ? ?// true

? ? fmt.Println(empty_map ==nil)? ? //false,空的的map不等價于nil(map的零值狀態(tài))

? ? empty_map["test"] = 12? ? ? ? ? //執(zhí)行正常,空的的map可以賦值設(shè)置元素

? ? null_map["test"] = 12? ? ? ? ? ? //panic: assignment to entry in nil map,無法給未初始化的map賦值設(shè)置元素

}

需要注意的幾點

1)map是無序的,每次打印出來的map都會不一樣,它不能通過index獲取,而必須通過key獲??;

2)map的長度是不固定的,也就是和slice一樣,也是一種引用類型;

3)內(nèi)置的len函數(shù)同樣適用于map,返回map擁有的key的數(shù)量;

4)map的值可以很方便的修改,通過numbers["one"]=11可以很容易的把key為one的字典值該為11;

5)map和其他基本類型不同,它不是thread-safe,在多個go-routine存取時,必須使用mutex lock機(jī)制;

map元素的同步更改

m := make(map[string]string)

m["hello"] = "Hello"

m1 := m

m1["hello"] = "World"

map是一種引用類型,如果兩個map同時指向一個底層,那么一個改變,另一個也相應(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 中所有的元素,不過需要注意因為 map本身是無序的,因此對于程序的每次執(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元素的增加和修改元素的語法一致,只需要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ù)鍵來刪除一個元素。需要強(qiáng)調(diào)delete函數(shù)沒有返回值,例如:

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 語言中map和slice,func一樣,不支持 == 操作符,就是不能直接比較。唯一合法的就是和nil作比較,判斷該map是不是零值狀態(tài)。

如果想自定義一個函數(shù),來比較兩個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

? ? }

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容