go map詳細(xì)使用方法

go map 比較深入的使用方案

參考blog: https://blog.golang.org/go-maps-in-action

現(xiàn)在基本上所有的編程語言都有自帶的map,或者dict,主要提供一個(gè)快速的查找,插入,刪除,具備與存儲(chǔ)體量無關(guān)的O(1)的性能,并且支持key上面的唯一性,
比如java里的HashMap,python里的Dictionary,scala里的各種Map等等。

go也原生提供了一個(gè)類似的數(shù)據(jù)類型,就叫做map。首先它是個(gè)mutable的,也就是說,可以隨時(shí)對(duì)其進(jìn)行修改。其次,它不是線程安全的。所以等價(jià)于java里的HashMap。

申明和初始化

map[KeyType]ValueType

這里的KeyType代表map的key類型,一定要是 comparable 的,而ValueType可以是任意的類型,甚至包括其他的內(nèi)嵌的map
比如

var m map[string]int

這里的keyType是string,valueType就是int

map在go里是屬于reference type,也就是作為方法的型參或者返回類型的是時(shí)候,傳遞也是這個(gè)reference的地址。不是map的本體。其次,這個(gè)map在申明的時(shí)候是nil map,需要如果沒有初始化,那么就是nil
對(duì)于這個(gè)nil的map,可以對(duì)其進(jìn)行任意的取值,返回都是(nil,err),但是如果對(duì)其設(shè)置一個(gè)新的值,就會(huì)panic
A nil map behaves like an empty map when reading, but attempts to write to a nil map will cause a runtime panic; don't do that

所以需要先初始化,方法1:

m = make(map[string]int)

方法二:

var m map[string]int = map[string]int{"hunter":12,"tony":10}

或者初始化一個(gè)空的map

m = map[string]int{}

讀取

i := m["route"]

如果route存在,就返回那個(gè)值,如果不存在,返回0值,也就是說,根據(jù)這個(gè)value的類型,返回缺省值,比如string,就返回“”,int 就返回0

刪除

delete(m,"route")

如果route存在,刪除成功,否則什么都沒有發(fā)生
因?yàn)樽x取在不存在key的時(shí)候返回0值,為了區(qū)分是否成功,可以采用如下手段

i, ok := m["route"]

遍歷

for key, value := range m {
    fmt.Println("Key:", key, "Value:", value)
}

稍微高級(jí)點(diǎn)的用法

利用0值,因?yàn)楫?dāng)從map中讀取一個(gè)不存在的key的時(shí)候,返回0值,有時(shí)候很麻煩,有時(shí)候也可以很巧妙的利用起來,參考原文英文中的例子

type Node struct {
    Next  *Node
    Value interface{}
}
var first *Node

func main(){
visited := make(map[*Node]bool)
    for n := first; n != nil; n = n.Next {
        if visited[n] {
            fmt.Println("cycle detected")
            break
        }
        visited[n] = true
        fmt.Println(n.Value)
    }
}

這是一個(gè)檢測單向鏈表是否有環(huán)的比較笨的辦法,原理就是利用map判斷這個(gè)key為*Node的值在map中是否出現(xiàn)過來確定是否有環(huán)。
這里的visited就是map,從這里我們可以看到,指針類型也是comparable的,所以可以作為keytype,其次,調(diào)用if語句中的visited[n]的時(shí)候,我們巧妙的利用了bool類型的0值就是false這個(gè)原理,來判斷這個(gè)keytype是否已經(jīng)出現(xiàn)。

還是原文中的例子:

    type Person struct {
        Name  string
        Likes []string
    }
    var people []*Person

    likes := make(map[string][]*Person)
    for _, p := range people {
        for _, l := range p.Likes {
            likes[l] = append(likes[l], p)
        }
    }

    for _, p := range likes["cheese"] {
        fmt.Println(p.Name, "likes cheese.")
    }

我們有一個(gè)自定義的struct,Person,里面存了人的名字和他/她的愛好,現(xiàn)在我們要寫一個(gè)簡單的小程序,把所有的people(人員)按照相同興趣進(jìn)行分類

我們這里的代碼就是利用兩個(gè)go里的特征,
1, range對(duì)于非nil的map,可以進(jìn)行遍歷,但是如果是nil的map(也就是沒有初始化的map),默認(rèn)按照空的map處理,也就是不運(yùn)行for循環(huán)的邏輯代碼
2, append支持非nil和nil 的map,都能進(jìn)行成功的append。這樣,就能簡化代碼

剛才提到map里的keytype必須是comparable的,go的文檔里有明確的定義:
The language spec defines this precisely, but in short, comparable types are boolean, numeric, string, pointer, channel, and interface types, and structs or arrays that contain only those types.

Notably absent from the list are slices, maps, and functions;

these types cannot be compared using ==, and may not be used as map keys.

線程安全(goroutine)

前面提到go的map不是線程安全的,因此需要加鎖,一般的方法是,定義一個(gè)embeded的struct,類似于子類

var counter = struct{
    sync.RWMutex
    m map[string]int
}{m: make(map[string]int)}

讀的時(shí)候,調(diào)用讀鎖

counter.RLock()
n := counter.m["some_key"]
counter.RUnlock()
fmt.Println("some_key:", n)

寫的時(shí)候,寫鎖

counter.Lock()
counter.m["some_key"]++
counter.Unlock()

# 讀取順序
go的map是hashmap,所以讀取遍歷的順序是不保證的,如果業(yè)務(wù)需要保證key的遍歷順序,建議將key單獨(dú)保存到一個(gè)slice里

import "sort"

var m map[int]string
var keys []int
for k := range m {
    keys = append(keys, k)
}
sort.Ints(keys)
for _, k := range keys {
    fmt.Println("Key:", k, "Value:", m[k])
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容