1. 程式人生 > 其它 >Go語言學習13-map

Go語言學習13-map

0x00 定義

對映(map),Go語言中內建的一種型別,它將鍵值對相關聯,我們可以通過鍵key來獲取對應的值value。類似其他語言的集合。

map是一種無序的基於key-value的資料結構,Go語言中的map是引用型別,必須初始化才能使用。

鍵值對:一對匹配的資訊

Go語言中 map的定義語法如下:

map[KeyType]ValueType

其中,

  • KeyType:表示鍵的型別。
  • ValueType:表示鍵對應的值的型別。

map型別的變數預設初始值為nil,需要使用make()函式來分配記憶體。語法為:

make(map[KeyType]ValueType, [cap])

其中cap表示map的容量,該引數雖然不是必須的,但是我們應該在初始化map的時候就為其指定一個合適的容量。

PS:key、value型別:bool、數字、string、指標、channel、還可以是隻包含前面幾個型別的介面、結構體、陣列

PS:key通常為int、string型別、value通常為數字(整數、浮點數)、string、map、結構體

PS:slice、map、function不可

0x01 map基本使用

map中的資料都是成對出現的,map的基本使用示例程式碼如下:

func main() {
	scoreMap := make(map[string]int, 8)	//一次性定義好容量,有利於程式的快捷
	scoreMap["張三"] = 90
	scoreMap["小明"] = 100
	fmt.Println(scoreMap)
	fmt.Println(scoreMap["小明"])
	fmt.Printf("type of a:%T\n", scoreMap)
}

輸出:

map[小明:100 張三:90]
100
type of a:map[string]int

map也支援在宣告的時候填充元素,例如:

func main() {
	userInfo := map[string]string{
		"username": "沙河小王子",
		"password": "123456",
	}
	fmt.Println(userInfo) //
}

0x02 判斷某個鍵是否存在

Go語言中有個判斷map中鍵是否存在的特殊寫法,格式如下:

value, ok := map[key]

舉個例子:

func main() {
	var m1 map[string]int //這是map初始化
	//fmt.Println(m1 == nil) 返回的是一個true,說明此為空,還未初始化,在記憶體中沒有開闢空間
	m1 = make(map[string]int, 10) //可以自動擴容,但是初始化的時候最好估算好該map的容量,避免在程式執行期間再動態擴容
	//10就是鍵值對的數量
	m1["beijing"] = 1
	m1["shanghai"] = 2
	m1["jiwuming"] = 35
	fmt.Println(m1)
	fmt.Println(m1["beijing"])
	value, ok := m1["nihao"] //約定成俗,用ok接收別的返回的bool值!如果key存在ok為true,v為對應的值;不存在ok為false,v為值型別的零值
	if !ok {
		fmt.Println("查無此人!")
	} else {
		fmt.Println(value)
	}
}

0x03 map的遍歷

Go語言中使用for range遍歷map。

func main() {
	scoreMap := make(map[string]int)
	scoreMap["張三"] = 90
	scoreMap["小明"] = 100
	scoreMap["娜扎"] = 60
	for k, v := range scoreMap {
		fmt.Println(k, v)
	}
}

但我們只想遍歷key的時候,可以按下面的寫法:

func main() {
	scoreMap := make(map[string]int)
	scoreMap["張三"] = 90
	scoreMap["小明"] = 100
	scoreMap["娜扎"] = 60
	for k := range scoreMap {
		fmt.Println(k)
	}
}

注意: 遍歷map時的元素順序與新增鍵值對的順序無關。

0x03 特點

1、map集合在使用前一定要make

2、map的key-value是無序的

3、key是不可以重複的,如果遇到重複,後一個value會覆蓋之前的

4、value是可以重複的

【4】三種map建立方式

方式一:var然後make函式(不推薦,瞭解)

var a map[int]string
	//只宣告map,記憶體是沒有分配空間的
	//必須通過make函式進行初始化才能分配空間
	a = make(map[int]string, 10) //map可以存放10個鍵值對
	a[2000] = "張三"
	a[2001] = "李四"

方式二:推斷

b := make(map[int]string)
b[1] = "beijing"
b[2] = "shanghai"
b[3] = "guagndong"

方式三:類似json格式的

c := map[int]string{
    1 : "beijing",
    2 : "shanghai",
    3 : "guangdong",		//一定要注意逗號,千萬不能忘掉
}

【5】map的增刪改

增加與修改

使用delete()函式刪除鍵值對

使用delete()內建函式從map中刪除一組鍵值對,delete()函式的格式如下:

delete(map, key)

其中,

  • map:表示要刪除鍵值對的map
  • key:表示要刪除的鍵值對的鍵

示例程式碼如下:

func main(){
	scoreMap := make(map[string]int)
	scoreMap["張三"] = 90
	scoreMap["小明"] = 100
	scoreMap["娜扎"] = 60
	delete(scoreMap, "小明")//將小明:100從map中刪除
	for k,v := range scoreMap{
		fmt.Println(k, v)
	}
}
	delete(b, 2)
	delete(b, 5)
	fmt.Println(b)

注意:清空操作

1、刪除map中的所有key,沒有一個專門的方法一次性刪除,可以遍歷一下key,逐個刪除;或者手動一個一個刪除

2、不過上面這個方法過於麻煩,所以我們怎麼做呢,就是通過map = make(...),make一個新的,讓原來的成為垃圾,被gc回收

查詢

value, flag := b[4]
fmt.Println(value, flag)

按照指定順序遍歷map

package main

import (
	"fmt"
	"math/rand"
	"sort"
	"time"
)

func main() {
	rand.Seed(time.Now().UnixNano())//當前時間的納秒輸出,並且隨機化
	scoreMap := make(map[string]int)	//新建一個map
	for i := 0; i < 100; i++ {		
		key := fmt.Sprintf("Stu%02d", i)	//for迴圈輸出99個學生名稱,並賦予給key變數
		value := rand.Intn(100)				//隨機生成1-100賦值給value變數
		scoreMap[key] = value				//將scoreMap的key和value一一對應起來
	}
	// fmt.Println(scoreMap)
	//將結果列印到切片中
	var keys = make([]string, 0, 200)
	for key := range scoreMap {
		keys = append(keys, key)
	}
	//隨後對切片進行排序
	sort.Strings(keys)
	//按照排序後的keys進行遍歷
	for _, key := range keys {
		fmt.Println(key, scoreMap[key])
	}

}

map與slice結合

map和slice都要初始化

下面這段程式碼,可以看到,make函式製作了一個切片,這個切片是map[int]string型別的。下方這個panic錯誤是因為make了一個切片,沒有任何的數值,所以我們需要新建一個數值,來保證不會顯示索引越界錯誤。

func main() {
	m1 := make([]map[int]string, 0, 10)
	m1[0][100] = "beijing"
	fmt.Println(m1)
}

當我將make函式裡面的長度變成1,就會顯示nil,為空,是因為map並沒有初始化,也就是賦值之類的。所以需要對map進行初始化。

func main() {
	m1 := make([]map[int]string, 0, 10)
	m1[0][100] = "beijing"
	fmt.Println(m1)
}

對map進行初始化

func main() {
	m1 := make([]map[int]string, 10, 10)
	m1[0] = make(map[int]string, 1)	//m1第一個map進行初始化
	m1[0][10] = "beijing"		//m1第一個map的值賦值,因為一共有10個map,剩餘9個就預設是空值
	fmt.Println(m1)
}

元素為map型別的切片

下面的程式碼演示了切片中的元素為map型別時的操作:

func main() {
	var mapSlice = make([]map[string]string, 3)
	for index, value := range mapSlice {
		fmt.Printf("index:%d value:%v\n", index, value)
	}
	fmt.Println("after init")
	// 對切片中的map元素進行初始化
	mapSlice[0] = make(map[string]string, 10)
	mapSlice[0]["name"] = "小王子"
	mapSlice[0]["password"] = "123456"
	mapSlice[0]["address"] = "沙河"
	for index, value := range mapSlice {
		fmt.Printf("index:%d value:%v\n", index, value)
	}
}

值為切片型別的map

啥意思?就是平時我們新建map的時候,是make(map[int]string),這回把string替換成[]string切片型別。

m2 := make(map[string][]string, 10)
	m2["beijing"] = []string{"朝陽區", "海淀區", "東城區", "房山區", "昌平區"}
	m2["shanghai"] = []string{"上海外灘", "莆田區", "老八區"}
	fmt.Println(m2)

總結

舉上面這兩個例子的目的是為了提醒,map和切片在定義後,一定要初始化!

練習題

  1. 寫一個程式,統計一個字串中每個單詞出現的次數。比如:”how do you do”中how=1 do=2 you=1。

    思路總結一下:

    • 先字串切割成切片,然後遍歷這些切片到map
    • 再判斷每個單詞是否在map中
    • 累計一下每個單詞出現的次數
  2. 觀察下面程式碼,寫出最終的列印結果。

func main() {
	type Map map[string][]int	//定義Map的型別為 map[string][]int,也就是值為切片的map型別
	m := make(Map)		//初始化一個Map,賦予變數給m
	s := []int{1, 2}	//s定義為一個具有兩個值的切片
    s = append(s, 3)	//新增一個數值,即s = []int{1,2,3}
	fmt.Printf("%+v\n", s)	//列印s
	m["q1mi"] = s		//m的qlmi鍵對應值為s
	s = append(s[:1], s[2:]...)	//刪除s[1]=2
    fmt.Printf("%+v\n", s)		//s={1,3}
    fmt.Printf("%+v\n", m["q1mi"])	//m["qlmi"]=[]int{1,2,3}
}