Golang映射類型map


在這裏插入圖片描述

1. map概述

Go語言中map字典類型時散列表(hash table)的實現,因爲Go語言中將map中的每個鍵都看作與其對應元素的索引,所以同一個map中的鍵都是唯一的.

映射map功能強大之處在於能夠基於鍵快速檢索數據

map類型可以寫成map[K]V ,其中所有的key必須是相同類型,所有的value也是相同類型,但是K也V可以是不同類型

2. map定義

2.1 map 聲明

var 變量名 map[key-type]value-type

  • 變量名 : 就是定義一個map類型的變量名稱
  • key-type : map類型數據鍵的類型 ,通常爲 int ,string類型
  • key-value : map類型數據值得類型,可以是int ,string ,bool,map,struct等等

map 的聲明不會分配內存,初始化需要make,分配內存之後才能使用

package main

import "fmt"

func main(){
	// 聲明一個鍵爲int型,值爲string型的map
	var a map[int]string
	// 聲明一個鍵爲int型,值爲float型的map
	var b map[int]float64
	// 聲明一個鍵爲string型,值爲bool型的map
	var c map[string]bool
	// 聲明一個鍵爲string型,值爲map[int]string型的map
	var d map[string]map[int]string
	fmt.Println(a)
	fmt.Println(b)
	fmt.Println(c)
	fmt.Println(d)
}

$ go run main.go
map[]
map[]
map[]
map[]

2.2 map初始化

  • map 在使用前一定要make

初始化方式1

package main

import "fmt"

func main(){
	 var a map[int]string
	 a  = make(map[int]string,5)
	 a[1] = "a"
	 a[2] = "b"
	 a[5] = "e"
	 a[3] = "c"
	 a[6] = "f"
	 fmt.Println("a = ",a)

}

$ go run main.go
a =  map[1:a 2:b 3:c 5:e 6:f]

初始化方式2

package main

import "fmt"

func main(){
	// 這種寫法顯得很冗餘
	var b map[string]string = map[string]string{
		"A" :"left",
		"D" :"right",
		"W":"forward",
	}
	// 簡化寫法
	fmt.Println("b = ",b)
	var b2 = map[string]string{
		"A" :"left",
		"D" :"right",
		"W":"forward",
	}
	fmt.Println("b2 = ",b2)
}

$ go run main.go
b =  map[A:left D:right W:forward]
b2 =  map[A:left D:right W:forward]

初始化方式3

package main

import "fmt"

func main(){
	c := map[int]string{
		1:"狀元",
		2:"榜眼",
		3:"探花",
	}
	fmt.Println(c)
}

$ go run main.go
map[1:狀元 2:榜眼 3:探花]

初始化方式4

package main

import (
	"fmt"
)

func main(){
	e := make(map[string]string)
	e["beijing"] = "北京"
	e["shanghai"] = "上海"
	fmt.Println(e)
}

$ go run main.go
map[beijing:北京 shanghai:上海]

3. map 基本操作

3.1 增 和 改

map類型數據的增加和修改 ,僅當map中的key不存的時候是新增一個元素,當key存在再給它賦值那就是修改一個key的值了

package main

import "fmt"

func main() {
	var m = make(map[int]string)
	// 給一個map 新增成員
	m[1] = "America"
	m[2] = "China"
	m[3] = "Russia"
	fmt.Println(m)
	// 修改map的數據
	m[1] ,m[2] = m[2],m[1]
	m[3] = "Germany"
	fmt.Println(m)
}

$ go run main.go
map[1:America 2:China 3:Russia]
map[1:China 2:America 3:Germany]

3.2 刪除

使用Go語言內建函數delete()刪除map中的一組鍵值

對map執行刪除元素操作時安全的,即使元素不存在,delete刪除一個不存在元素返回率 0

package main

import "fmt"

func main() {
	var m = make(map[int]string)
	// 給一個map 新增元素
	m[1] = "America"
	m[2] = "China"
	m[3] = "Russia"
	fmt.Println(m)
	//刪除map中的一組鍵值
	delete(m,1)
	fmt.Println(m)

}

$ go run main.go
map[1:America 2:China 3:Russia]
map[2:China 3:Russia]

清空map中所有元素,在Go語言中沒有直接提供這樣的方法和函數,那麼清空一個map的唯一途徑是重新make一個新的map,讓原來的數據塊成爲"垃圾",由系統的GC執行垃圾回收

package main

import "fmt"

func main() {
	m := map[int]string{
		1:"北京",
		2:"上海",
		3:"廣州",
		4:"深圳",
	}
	fmt.Println(m)
	// 清空一個map
	m = make(map[int]string)
	fmt.Println(m)
}

$ go run main.go
map[1:北京 2:上海 3:廣州 4:深圳]
map[]

3.3 查找

在Go語言中map的查找設計的很簡潔明確

使用下標來訪問map中的元素的元素輸出兩個值,第一個是訪問元素的值,一個是報告元素是否存在的bool值,我們一般將這個bool值命名爲 ok

package main

import (
	"fmt"
)

func main(){
	var m = map[string]string{
		"name":"tom",
		"age":"99",
		"addr":"beijing",
		"work":"programer",
	}
	//判斷是否找到特定的鍵,只需要看第二個參數返回值是true或者false就可以
	//這裏的ok是一個變量,用來接收是否找到指定的鍵的標識
	name ,ok := m["name"]
	fmt.Println(ok) //true
	if ok{
		fmt.Println(name) // tom
	}
    //上面訪問map元素的寫法,可以合併成一條語句
	if addr,ok := m["addr"]; ok{
		fmt.Println(addr)
	}
	nickname,yes:= m["nickname"]
	fmt.Println(yes) // false
	fmt.Println(nickname)
}

$ go run main.go
true
tom
beijing
false

4. map遍歷

map的遍歷通過 for - range 結構來完成

映射map是無序的集合,所以沒有辦法預測鍵值對返回的順序,故而每次迭代map的順序也可能不一樣.

4.1 簡單map結構的遍歷

package main

import "fmt"

func main() {
	var m = map[string]string{
		"name": "tom",
		"age":  "99",
		"addr": "beijing",
		"work": "programmer",
	}
	for k,v := range m{
		fmt.Printf("key : %s\t value : %s\n",k,v)
	}
}


$ go run main.go
key : addr	 value : beijing
key : work	 value : programmer
key : name	 value : tom
key : age	 value : 99

4.2 複雜map結構的遍歷

package main

import "fmt"

func main(){
	var m = make(map[int]map[string]string)
	m[1] = make(map[string]string)
	m[1]["name"] = "tom"
	m[1]["age"] = "99"
	m[1]["addr"] = "bejing"
	m[2] = make(map[string]string)
	m[2]["name"] = "lucy"
	m[2]["age"] = "88"
	m[2]["addr"] = "shanghai"
	for k,v := range m{
		fmt.Println("k = ",k)
		for k1,v1 := range v{
			fmt.Println(k1,"===",v1)
		}
	}
}
$ go run main.go
k =  1
name === tom
age === 99
addr === bejing
k =  2
name === lucy
age === 88
addr === shanghai

4.3 map的比較

map和slice 一樣是不能進行比較的,唯一能做的比較是和 nil 比較.

要判斷兩個map是否擁有相同的鍵和值,必須寫循環判斷

示例代碼只展示一個思路

package main

import "fmt"

func mapCompare(x, y map[string]string) 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
}
func main() {
	var xm = map[string]string{
		"name": "tom",
		"addr": "beijing",
	}
	var ym = map[string]string{
		"name": "tom",
		"addr": "beijing",
	}
	// map 類型只能和nil 直接比較
	if xm == nil || ym == nil {
		fmt.Println("map xm or ym is nil")
	} else {
		fmt.Println("map xm and ym is ok")
	}
	// 比較兩個map是否擁有相同的鍵值
	 if ok := mapCompare(xm,ym) ;ok{
	 	fmt.Println("xm 和 ym 有相同的鍵和值")
	 }
}
$ go run main.go
map xm and ym is ok
xm 和 ym 有相同的鍵和值

5. 補充點

5.1 併發讀寫

併發對一個map進行讀寫操作會引發競態問題

package main

func main() {
	var m = make(map[int]int)
	// 併發函數不斷對map進行讀寫的時候,發生競態問題
	go func() {
		for {
			m[1] = 1
		}
	}()
	go func() {
		for {
			_ = m[1]
		}
	}()
	for {

	}
}

$ go run main.go
fatal error: concurrent map read and map write

面臨這樣的問題,可以採取的一個方案是使用 sync.Map 這是sync包的一個特殊結構,與map很不同,相比較map 是損失了性能,解決的併發讀寫問題,如果沒有併發讀寫問題,直接使用map即可

package main

import (
	"fmt"
	"sync"
	"time"
)

func main() {
	// 定義一個sync.Map類型
	var sm sync.Map
	// 給 sm 添加元素
	sm.Store("name", "zhangshan")
	sm.Store("addr", "bejing")
	sm.Store("job", "programmer")
	//獲取一個元素
	name, ok := sm.Load("name")
	if ok {
		fmt.Println(name)
	}
    // 刪除一個元素(安全刪除)
	sm.Delete("nameNone")
	// 遍歷sm中的元素
	sm.Range(func(key, value interface{}) bool {
		fmt.Println(key,"===>",value)
		return true
	})
	// 測試併發讀寫使用
	go func() {
		for {
			t := time.Now().Unix()
			sm.Store("name", t)
		}
	}()
	go func() {
		for {
			name, _ := sm.Load("name")
			fmt.Println(name)
		}
	}()
	for {

	}
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章