每個語言基本都有類似於map的容器,C++有map和unordered_map,map的底層是紅黑樹,unordered_map的底層是哈希表。那麼GO語言中的map它的底層就是哈希表了。下面來討論GO語言中的map的使用
定義map
可以用下面的方式直接定義
func main() {
m1 := map[string]string{
"January" : "1",
"February" : "2",
"March" : "3",
}
fmt.Println(m1)
}
其中定義的第一行,map表示變量m是一個map,[string]表示它的key是一個string,後面跟着的string表示它的值是一個string,花括號{}內部是給這個map添加的一些元素
另外也可以用make來定義
m2 := make(map[string]string)
上面創建了一個空的map,注意空的map和nil的區別
func main() {
m2 := make(map[string]string) //空map
var m3 map[string]string //nil
m2["April"] = "4" //添加成功
m3["April"] = "4" //運行時出錯
fmt.Println(m2)
fmt.Println(m3)
}
用make創建出來的是一個空map,而如果像下面一行那樣只是用var來聲明,那麼其實這個m3是一個nil,nil是map的默認零值,這個和Slice一樣,往nil的map中插入數據會出錯
panic: assignment to entry in nil map
所以不推薦使用var來只聲明而不定義一個map,聲明瞭還是需要用make來給map定義。
向map中添加元素
添加元素有點像C++的map重載的[]的方法,直接使用[]來像map中添加和修改元素,用過C++的map的應該很好理解
func main() {
m1 := map[string]string{
"January" : "1",
"February" : "2",
"March" : "3",
}
fmt.Println(m1)
m1["April"] = "4" //添加成功
fmt.Println(m1)
m1["January"] = "一月"
fmt.Println(m1) //修改成功
}
輸出結果
map[January:1 February:2 March:3]
map[January:1 February:2 March:3 April:4]
map[January:一月 February:2 March:3 April:4] //修改成功
遍歷map
range也可以用在對map的遍歷,事實上許多容器都可以用range來遍歷
func main() {
m1 := map[string]string{
"January" : "1",
"February" : "2",
"March" : "3",
}
m1["April"] = "4" //添加成功
for k,v := range m1 {
fmt.Println(k,v)
}
}
輸出結果
January 1
February 2
March 3
April 4
range同樣返回兩個值,第一個表示map的key,第二個表示map的value,和數組的使用方式一樣,如果我們只想要value,那麼需要k的位置需要用下劃線_代替。
至於爲什麼輸出的結果順序爲什麼不是有序的,即如果是有序的,那麼輸出的結果是按key的大小排序的(即April,February,March,Month),這裏學過C++的應該瞭解過map和unordered_map的區別,map的底層是紅黑樹,遍歷一棵紅黑樹的時候,我們遍歷的時候是按大小比較遍歷的,而GO的map相當於unordered_map,底層是哈希表實現的,哈希表不能保證插入進來的元素的有序性,它是無序的,但是它的插入效率要比紅黑樹快一點,因此就是犧牲有序性來提升效率。
獲取map中的元素
獲取元素同樣通過[]來獲取,只不過注意一下它的返回值。
func main() {
m1 := map[string]string{
"January" : "1",
"February" : "2",
"March" : "3",
}
month := m1["January"]
fmt.Println(month)
}
這邊通過[]獲取到了key爲January的value,那麼這個時候如果想要獲取一個不存在的key的value會怎麼樣呢?
func main() {
m1 := map[string]string{
"January" : "1",
"February" : "2",
"March" : "3",
}
month := m1["April"]
fmt.Println(month)
fmt.Println(m1)
}
輸出結果
map[March:3 January:1 February:2]
輸出了一個空串,所以如果key不存在,那麼返回的是value的默認零值。我們之後也打印了一下m1,發現m1中並沒有這個April。其實學過C++的在這裏地方會有一點問題,因爲在這種情況下,C++的map中是會將April這個key插入到map中的。
來看一下下面的C++代碼
#include <iostream>
#include <map>
using namespace std;
int main(){
map<string,string> m1;
m1["January"] = "1";
m1["February"] = "2";
m1["March"] = "3";
for(auto i : m1)
cout << i.first << ":" << i.second << endl;
cout << endl;
string month = m1["April"];
for(auto i : m1)
cout << i.first << ":" << i.second << endl;
return 0;
}
輸出結果
February:2
January:1
March:3
April:
February:2
January:1
March:3
可見,GO語言在這個地方和C++就不同了,需要注意,GO只有在真正地執行添加元素的操作纔會往map中添加元素,而C++只要使用到對應的key就會往map中添加這個key
另外,如果想要判斷key在map中是否存在怎麼做呢?GO通過下面的方式來解決
func main() {
m1 := map[string]string{
"January" : "1",
"February" : "2",
"March" : "3",
}
month,ok:= m1["April"]
fmt.Println(month,ok)
month,ok = m1["January"]
fmt.Println(month,ok)
}
輸出結果
false
1 true
其實調用[]返回的是兩個值,第一個是key對應的value,第二個返回值表示這個key是否存在,是一個bool類型。所以,如果我們只想判斷這個key是否存在,就可以用下劃線_代替前面的value不接收那個參數
刪除map中的元素
刪除map中的元素使用內置函數delete
func main() {
m1 := map[string]string{
"January" : "1",
"February" : "2",
"March" : "3",
}
month,ok := m1["January"]
fmt.Println(month,ok)
delete(m1,"January")
month,ok = m1["January"]
fmt.Println(month,ok)
}
輸出結果
1 true
false
這樣就把key爲January的元素刪除了
獲取map中元素的個數
獲取元素個數使用len內置函數
func main() {
m1 := map[string]string{
"January" : "1",
"February" : "2",
"March" : "3",
}
fmt.Println(len(m1))
}
輸出結果
3
map是引用類型
func change(m1 map[string]string){
m1["January"] = "一月"
}
func main() {
m1 := map[string]string{
"January" : "1",
"February" : "2",
"March" : "3",
}
change(m1)
fmt.Println(m1)
}
輸出結果
map[January:一月 February:2 March:3]
因爲map是引用類型,所以它作爲參數傳給函數參數的時候,是引用傳遞,因此在函數內部對map的改變也會影響到原有的map。