go map 基本操作


目錄

  • map中的key的數據類型
    • key的幾種數據類型舉例
  • map基本操作
    • map創建
    • map增刪改查
    • map遍歷
      • map遍歷易錯點舉例

  • go中的map是hash表的一個引用,類型寫為:map[key]value,其中的key, value分別對應一種數據類型,如map[string]string
  • 要求所有的key的數據類型相同,所有value數據類型相同(注:key與value可以有不同的數據類型)

map中的key的數據類型

  • map中的每個key在keys的集合中是唯一的,而且需要支持 == or != 操作
  • key的常用類型:int, rune, string, 結構體(每個元素需要支持 == or != 操作), 指針, 基於這些類型自定義的類型

float32/64 類型從語法上可以作為key類型,但是實際一般不作為key,因為其類型有誤差

key的幾種數據類型舉例

	// m0 可以, key類型為string, 支持 == 比較操作
	{
		var m0 map[string]string // 定義map類型變量m0,key的類型為string,value的類型string
		fmt.Println(m0)
	}

	// m1 不可以, []byte是slice,不支持 == != 操作,不可以作為map key的數據類型
	{
		//var m1 map[[]byte]string // 報錯: invalid map key type []byte
		//fmt.Println(m1)

		// 准確說slice類型只能與nil比較,其他的都不可以,可以通過如下測試:
		// var b1,b2 []byte
		// fmt.Println(b1==b2) // 報錯: invalid operation: b1 == b2 (slice can only be compared to nil)
	}

	// m2 可以, interface{}類型可以作為key,但是需要加入的key的類型是可以比較的
	{
		var m2 map[interface{}]string
		m2 = make(map[interface{}]string)
		//m2[[]byte("k2")]="v2" // panic: runtime error: hash of unhashable type []uint8
		m2[123] = "123"
		m2[12.3] = "123"
		fmt.Println(m2)
	}

	// m3 可以, 數組支持比較
	{
		a3 := [3]int{1, 2, 3}
		var m3 map[[3]int]string
		m3 = make(map[[3]int]string)
		m3[a3] = "m3"
		fmt.Println(m3)
	}

	// m4 可以,book1里面的元素都是支持== !=
	{
		type book1 struct {
			name string
		}
		var m4 map[book1]string
		fmt.Println(m4)
	}

	// m5 不可以, text元素類型為[]byte, 不滿足key的要求
	{
		// type book2 struct {
		// 	name string
		// 	text []byte //沒有這個就可以
		// }
		//var m5 map[book2]string //invalid map key type book2
		//fmt.Println(m5)
	}


map基本操作

map創建

  • 兩種創建的方式:一是通過字面值;二是通過make函數
	// 1 字面值
	{
		m1 := map[string]string{
			"m1": "v1", // 定義時指定的初始key/value, 后面可以繼續添加
		}
		_ = m1

	}

	// 2 使用make函數
	{
		m2 := make(map[string]string) // 創建時,里面不含元素,元素都需要后續添加
		m2["m2"] = "v2"               // 添加元素
		_ = m2

	}

	// 定義一個空的map
	{
		m3 := map[string]string{}
		m4 := make(map[string]string)
		_ = m3
		_ = m4
	}

map增刪改查

	// 創建
	m := map[string]string{
		"a": "va",
		"b": "vb",
	}
	fmt.Println(len(m)) // len(m) 獲得m中key/value對的個數

	// 增加,修改
	{
		// k不存在為增加,k存在為修改
		m["c"] = ""
		m["c"] = "11"                      // 重復增加(key相同),使用新的值覆蓋
		fmt.Printf("%#v %#v\n", m, len(m)) // map[string]string{"a":"va", "b":"vb", "c":"11"} 3
	}

	// 查
	{
		// v := m[k] // 從m中取鍵k對應的值給v,如果k在m中不存在,則將value類型的零值賦值給v
		// v, ok := m[k] // 從m中取鍵k對應的值給v,如果k存在,ok=true,如果k不存在,將value類型的零值賦值給v同時ok=false
		// 查1 - 元素不存在
		v1 := m["x"] //
		v2, ok2 := m["x"]
		fmt.Printf("%#v %#v %#v\n", v1, v2, ok2) // "" "" false

		// 查2 - 元素存在
		v3 := m["a"]
		v4, ok4 := m["a"]
		fmt.Printf("%#v %#v %#v\n", v3, v4, ok4) //"va" "va" true
	}

	// 刪, 使用內置函數刪除k/v對
	{
		// delete(m, k) 將k以及k對應的v從m中刪掉;如果k不在m中,不執行任何操作
		delete(m, "x")                     // 刪除不存在的key,原m不影響
		delete(m, "a")                     // 刪除存在的key
		fmt.Printf("%#v %#v\n", m, len(m)) // map[string]string{"b":"vb", "c":"11"} 2
		delete(m, "a")                     // 重復刪除不報錯,m無影響
		fmt.Printf("%#v %#v\n", m, len(m)) /// map[string]string{"b":"vb", "c":"11"} 2
	}

map遍歷

  • 遍歷的順序是隨機的
  • 使用for range遍歷的時候,k,v使用的同一塊內存,這也是容易出現錯誤的地方
	m := map[string]int{
		"a": 1,
		"b": 2,
	}
	for k, v := range m {
		fmt.Printf("k:[%v].v:[%v]\n", k, v) // 輸出k,v值
	}

map遍歷易錯點舉例

由於遍歷的時候,遍歷v使用的同一塊地址,同時這塊地址是臨時分配的。雖然v的地址沒有變化,但v的內容在一直變化,當遍歷完成后,v的內容是map遍歷時最后遍歷的元素的值(map遍歷無序,每次不確定哪個元素是最后一個元素)。當程序將v的地址放入到slice中的時候,slice在不斷地v的地址插入,由於v一直是那塊地址,因此slice中的每個元素記錄的都是v的地址。因此當打印slice中的內容的時候,都是同一個值

	m := map[string]int{
		"a": 1,
		"b": 2,
	}
	var bs []*int
	for k, v := range m {
		fmt.Printf("k:[%p].v:[%p]\n", &k, &v) // 這里的輸出可以看到,k一直使用同一塊內存,v也是這個狀況
		bs = append(bs, &v) // 對v取了地址
	}

	// 輸出
	for _, b := range bs {
		fmt.Println(*b) // 輸出都是1或者都是2
	}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM