1. 程式人生 > 其它 >Go語言 對映(map)

Go語言 對映(map)

Go語言  對映(map)

 

1. 什麼是 map
2. 建立 map
3. 訪問 map
4. nil map和空map
5. map中元素的返回值
6. len()和delete()
7. 測試map中元素是否存在
8. 迭代遍歷 map
9. 獲取map中所有的key
10. 傳遞map給函式

 

 

1. 什麼是 map

Map 是一種無序的鍵值對的集合。Map 最重要的一點是通過 key 來快速檢索資料,key 類似於索引,指向資料的值

Map 是無序的,我們無法決定它的返回順序,這是因為 Map 是使用 hash 表來實現的

Map 是引用型別,必須初始化才能使用。其中,key的型別除了切片等引用型別,其他型別都可以;而value則可使用所有型別的值

 

2. 創建 map

可以通過make()建立map,它會先建立好底層資料結構,然後再建立map,並讓map指向底層資料結構

my_map := make(map[string]int)

[string]表示map的key的資料型別
int表示key對應的值

 

直接通過大括號建立並初始化賦值:

// 空map
my_map := map[string]string{}

// 初始化賦值
my_map := map[string]string{"Red": "#da1337","Orange": '#e95a22"}

// 格式化賦值
my_map := map[string]int{
"Java":11,
"Perl":8,
"Python":13, // 注意結尾的逗號不能少
}

  

注意:

其中map的key可以是任意內建的資料型別(如int),或者其它可以通過 == 進行等值比較的資料型別,如interface和指標可以,slice、陣列、map、struct型別都不能作為key ,並且key必須唯一。

但value基本可以是任意型別,例如巢狀一個slice到map中:

my_map := map[string][]int{}

 

3. 訪問 map

訪問map中的元素時,指定它的key即可,注意string型別的key必須加上引號:

package main

import "fmt"

func main() {
	my_map := map[string]int{
		"1": 10,
		"2": 20,
		"3": 30,
		"4": 40,
	}

	//訪問
	fmt.Println(my_map["1"])
	fmt.Println(my_map)

	fmt.Println("")

	//賦值已有的key & value
	my_map["2"] = 50
	fmt.Println(my_map["2"])
	fmt.Println(my_map)

	fmt.Println("")

	//賦值新的key&value
	my_map["5"] = 66
	fmt.Println(my_map["5"])
	fmt.Println(my_map)
}


//輸出結果
10
map[1:10 2:20 3:30 4:40]

50
map[1:10 2:50 3:30 4:40]

66
map[1:10 2:50 3:30 4:40 5:66]

  

4. nil map和空map

空map是不做任何賦值的map:

// 空map
package main

import "fmt"

func main() {
	my_map := map[string]string{}

	fmt.Println(my_map)
}

//輸出結果
map[]

  

nil map,它將不會做任何初始化,不會指向任何資料結構:

// nil map
var my_map map[string]string

 


nil map和empty map的關係,就像nil slice和empty slice一樣,兩者都是空物件,未儲存任何資料,但前者不指向底層資料結構,後者指向底層資料結構,只不過指向的底層物件是空物件。

使用println輸出看下即可知道:

package main

func main() {
	var nil_map map[string]string
	println(nil_map)

	emp_map := map[string]string{}
	println(emp_map)
}

  

輸出結果:

0x0
0xc04204de38


所以,map型別實際上就是一個指標。

 

5. map中元素的返回值

當訪問map中某個元素的時候,有兩種返回值的格式:

value := my_map["key"]
value,exists := my_map["key"]


第一種很好理解,就是檢索map中key對應的value值。如果key不存在,則value返回值對應資料型別的0。例如int為數值0,布林為false,字串為空""。

第二種不僅返回key對應的值,還根據key是否存在返回一個布林值賦值給exists變數。所以,當key存在時,value為對應的值,exists為true;當key不存在,value為0(同樣是各資料型別所代表的0),exists為false。

看下例子:

package main

import "fmt"

func main() {
	my_map := map[string]int{
		"1": 10,
		"2": 20,
		"3": 30,
		"4": 40,
	}
	a := my_map["1"]
	b, exists1 := my_map["2"]
	c, exists2 := my_map["5"]
	fmt.Println(a)
	fmt.Println(b, exists1)
	fmt.Println(c, exists2)
}

  

上面將輸出如下結果:

10
20 true
0 false


在Go中設定類似於這種多個返回值的情況很多,即便是自己編寫函式也會經常設定它的exists屬性。

6. len()和delete()

len()函式用於獲取map中元素的個數,即有多個少key。delete()用於刪除map中的某個key。

package main

import "fmt"

func main() {
	my_map := map[string]int{
		"1": 10,
		"2": 20,
		"3": 30,
		"4": 40,
	}

	fmt.Printf("刪除前長度為%d\n", len(my_map))
	delete(my_map, "1")
	fmt.Printf("刪除後長度為%d", len(my_map))
}

//輸出結果如下
刪除前長度為4
刪除後長度為3

  


7. 測試map中元素是否存在

兩種方式可以測試map中是否存在某個key:

① 根據map元素的第二個返回值來判斷
② 根據返回的value是否為0(不同資料型別的0不同)來判斷

方式一:直接訪問map中的該元素,將其賦值給兩個變數,第二個變數就是元素是否存在的修飾變數。

package main

import "fmt"

func main() {
	my_map := map[string]int{
		"1": 10,
		"2": 20,
		"3": 30,
		"4": 40,
	}
	
//方法1
/* value, exists := my_map["1"]
	if exists {
		fmt.Println("存在", value)
	}
 */


	//方法2
	if value, exists := my_map["1"]; exists {
		fmt.Printf("值存在, value=%d", value)
	}
}

//輸出結果如下
值存在, value=10

  

方式二:根據map元素返回的value判斷。因為該map中的value部分是int型別,所以它的0是數值的0。

package main

import "fmt"

func main() {
	my_map := map[string]int{
		"1": 10,
		"2": 20,
		"3": 30,
		"4": 40,
	}

	value := my_map["5"]
	if value == 0 {
		fmt.Println("不存在")
	}
}


//輸出結果如下
不存在

  

如果map的value資料型別是string,則判斷是否為空:

package main

import "fmt"

func main() {
	my_map := map[string]string{
		"1": "book",
		"2": "games",
		"3": "computer",
	}

	value := my_map["5"]
	if value == "" {
		fmt.Println("不存在")
	}
}

//輸出結果如下
不存在

  

由於map中的value有可能本身是存在的,但它的值為0,這時就會出現誤判斷。例如下面的"3",它已經存在,但它對應的值為0

package main

import "fmt"

func main() {
	my_map := map[string]int{
		"1": 22,
		"2": 11,
		"3": 0,
	}

	value := my_map["3"]
	if value == 0 {
		fmt.Println("不存在")
	}
}

//輸出結果如下
不存在

  

所以,應當使用第一種方式進行判斷元素是否存在。

 

8. 迭代遍歷 map

因為map是key/value型別的資料結構,key就是map的index,所以range關鍵字對map操作時,將返回key和value。

package main

import "fmt"

func main() {
	my_map := map[string]int{
		"1": 22,
		"2": 11,
		"3": 0,
		"4": 55,
		"5": 66,
	}

	for k, v := range my_map {
		fmt.Printf("key=%s, value=%d\n", k, v)
	}
}


//輸出結果如下
key=1, value=22
key=2, value=11
key=3, value=0
key=4, value=55
key=5, value=66

  

如果range迭代map時,只給一個返回值,則表示迭代map的key:

package main

import "fmt"

func main() {
	my_map := map[string]int{
		"1": 22,
		"2": 11,
		"3": 0,
		"4": 55,
		"5": 66,
	}

	for key := range my_map {
		fmt.Println("key=", key)
	}
}


//輸出結果
key= 1
key= 2
key= 3
key= 4
key= 5

  

9. 獲取map中所有的key

Go中沒有提供直接獲取map所有key的函式。所以,只能自己寫,方式很簡單,range遍歷map,將遍歷到的key放進一個slice中儲存起來。

package main

import "fmt"

func main() {
	my_map := map[string]int{
		"Java":   11,
		"Perl":   8,
		"Python": 13,
		"Shell":  23,
	}

    // 儲存map中key的slice
    // slice型別要和map的key型別一致
	keys := make([]string,0,len(my_map))

    // 將map中的key遍歷到keys中
	for map_key,_ := range my_map {
		keys = append(keys,map_key)
	}

	fmt.Println(keys)
}

  

注意上面宣告的slice中要限制長度為0,否則宣告為長度4、容量4的slice,而這4個元素都是空值,而且後面append()會直接對slice進行一次擴容,導致append()後的slice長度為map長度的2倍,前一半為空,後一般才是map中的key。

 

10. 傳遞map給函式

map是一種指標,所以將map傳遞給函式,僅僅只是複製這個指標,所以函式內部對map的操作會直接修改外部的map。

例如,test()用於給map的key對應的值加1。

package main

import "fmt"

func main() {
	my_map := map[string]int{
		"1": 22,
		"2": 11,
		"3": 0,
		"4": 55,
		"5": 66,
	}
	fmt.Println("修改之前key=", my_map["3"])
	fmt.Println(my_map)

	fmt.Println("")

	test(my_map, "3")
	fmt.Println("修改之後key=", my_map["3"])
	fmt.Println(my_map)

}

func test(m map[string]int, key string) {
	m[key] += 1
}


//輸出結果如下
修改之前key= 0
map[1:22 2:11 3:0 4:55 5:66]

修改之後key= 1
map[1:22 2:11 3:1 4:55 5:66]