?1.map認識? ? ? ?
????????哈希表是一種巧妙并且實用的數據結構。它是一個無序的key/value對的集合,其中所有的key都是不同的,然后通過給定的key可以在常數時間復雜度內檢索、更新或者刪除對用的value。
? ? ? ? 在Go語言中,一個map就是一個哈希表的引用,map類型可以寫為map[K]V,其中K和V分別對應的key和value。map中所有的key都是相同的類型,所有的value也是相同的類型,但是key和value之間可以是不同的數據類型。其中K對應的key必須是支持==比較運算符的數據類型,所以map可以通過測試key是否相等來判斷是否已經存在。
?2.map的創建? ? ? ?
????????內置的make函數可以創建一個map:
ages := make(map[string]int) // mapping from strings to ints
????????我們也可以用map字面值的語法創建map,同時還可以指定一些最初的key/value:
package mainimport "fmt"func main() {ages := map[string]int{"alice": 31,"charlie": 34,}fmt.Println(ages)
}map[alice:31 charlie:34]
? ? ? ? 也可以用下列來創建map:
package mainimport "fmt"func main() {ages := make(map[string]int)ages["alice"] = 31ages["charlie"] = 34fmt.Println(ages)
}map[alice:31 charlie:34]
????????因此,另一種創建空的map的表達式是 map[string]int{} 。
3.map的引用
????????Map中的元素通過key對應的下標語法訪問:
package mainimport "fmt"func main() {ages := make(map[string]int)ages["alice"] = 31ages["charlie"] = 34fmt.Println(ages["alice"])
}31
? ? ? ? 我們也可以用內置的delete函數可以刪除元素:
package mainimport "fmt"func main() {ages := make(map[string]int)ages["alice"] = 31ages["charlie"] = 34delete(ages, "alice")fmt.Println(ages["alice"])fmt.Println(ages)
}0
map[charlie:34]
????????所有這些操作是安全的,即使這些元素不在map中也沒有關系;如果一個查找失敗將返回 value類型對應的零值,例如,即使map中不存在“bob”下面的代碼也可以正常工作,因為 ages["bob"]失敗時將返回0。
????????
package mainimport "fmt"func main() {ages := make(map[string]int)ages["alice"] = 31ages["charlie"] = 34ages["bob"] = ages["bob"] + 1fmt.Println(ages)
}map[alice:31 bob:1 charlie:34]
????????而且 x += y 和 x++ 等簡短賦值語法也可以用在map上,所以上面的代碼可以改寫成
ages["bob"] += 1
????????更簡單的寫法
ages["bob"]++
????????但是map中的元素并不是一個變量,因此我們不能對map的元素進行取址操作:
_ = &ages["bob"] // compile error: cannot take address of map element
? ? ? ??禁止對map元素取址的原因是map可能隨著元素數量的增長而重新分配更大的內存空間,從而 可能導致之前的地址無效。要想遍歷map中全部的key/value對的話,可以使用range風格的for循環實現,和之前的slice遍歷語法類似。下面的迭代語句將在每次迭代時設置name和age變量,它們對應下一個鍵/值對:
package mainimport "fmt"func main() {ages := make(map[string]int)ages["alice"] = 31ages["charlie"] = 34ages["bob"] = ages["bob"] + 1fmt.Println(ages)for name, age := range ages {fmt.Printf("%s\t%d\n", name, age)}
}map[alice:31 bob:1 charlie:34]
charlie 34
bob 1
alice 31
? ? ? ? Map的迭代順序是不確定的,并且不同的哈希函數實現可能導致不同的遍歷順序。在實踐中,遍歷的順序是隨機的,每一次遍歷的順序都不相同。這是故意的,每次都使用隨機的遍歷順序可以強制要求程序不會依賴具體的哈希函數實現。如果要按順序遍歷key/value對,我 們必須顯式地對key進行排序,可以使用sort包的Strings函數對字符串slice進行排序。下面是 常見的處理方式:
package mainimport ("fmt""sort"
)func main() {ages := make(map[string]int)ages["alice"] = 31ages["charlie"] = 34var names []stringfor name := range ages {names = append(names, name)}sort.Strings(names)for _, name := range names {fmt.Printf("%s\t%d\n", name, ages[name])}}alice 31
charlie 34
????????因為我們一開始就知道names的最終大小,因此給slice分配一個合適的大小將會更有效。下 面的代碼創建了一個空的slice,但是slice的容量剛好可以放下map中全部的key:
names := make([]string, 0, len(ages))
????????在上面的第一個range循環中,我們只關心map中的key,所以我們忽略了第二個循環變量。 在第二個循環中,我們只關心names中的名字,所以我們使用“_”空白標識符來忽略第一個循 環變量,也就是迭代slice時的索引。 map類型的零值是nil,也就是沒有引用任何哈希表。
????????在向map存數據前必須先創建map。 通過key作為索引下標來訪問map將產生一個value。如果key在map中是存在的,那么將得到 與key對應的value;如果key不存在,那么將得到value對應類型的零值,正如我們前面看到的 ages["bob"]那樣。這個規則很實用,但是有時候可能需要知道對應的元素是否真的是在map 之中。例如,如果元素類型是一個數字,你可以需要區分一個已經存在的0,和不存在而返回 零值的0,可以像下面這樣測試:
age, ok := ages["bob"]
if !ok { /* "bob" is not a key in this map; age == 0. */ }
或者
if age, ok := ages["bob"]; !ok { /* ... */ }
????????在這種場景下,map的下標語法將產生兩個值;第二個是一個布爾值,用于報告元素是否真 的存在。布爾變量一般命名為ok,特別適合馬上用于if條件判斷部分。 和slice一樣,map之間也不能進行相等比較;唯一的例外是和nil進行比較。要判斷兩個map是 否包含相同的key和value,我們必須通過一個循環實現:
func equal(x, y map[string]int) 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
}