首頁 >後端開發 >Golang >Go 中的地圖

Go 中的地圖

PHPz
PHPz原創
2024-07-23 00:48:33465瀏覽

Maps in Go

簡介

Go 合併了一個本地類型,它實作了稱為 map 的雜湊表。它是一種資料類型,由唯一鍵的集合和每個鍵的值的集合組成。
例如,它可以與其他語言中的字典進行比較,字典儲存鍵值對。這些值是使用鍵存取的,就像我們在上一篇文章中看到的陣列和切片一樣。
索引不限於數組或切片中的數字,而且元素沒有排序,因此如果我們列印地圖,如果我們不執行任何操作來覆蓋其列印並強制執行所需的順序,它將返回隨機順序。

地圖聲明與初始化

要宣告一個映射,它是用map[key]value完成的,其中key將是我們想要的鍵的類型(它必須是可比較的類型https://go.dev/ref/spec# Comparison_operators ) 和value 將是我們希望映射儲存在每個鍵中的類型,無論它是什麼類型,從int 到結構,或另一個映射,無論我們想要什麼。

與切片一樣,映射是引用類型,這表示映射的零值將為 nil。
發生這種情況是因為它下面有一個儲存鍵和值的雜湊表,它們只是它們的一個信封和抽象。

如果我們將其聲明為:

var m map[int]int

其價值將為零。

如果我們希望它的值為零,我們可以使用宣告:

m := map[int]int{}

我們甚至可以使用 make 函數像切片一樣初始化它。

m := make(map[string]string)

這樣做將使用適當的記憶體池初始化哈希映射,從而傳回指向該資料結構的映射。

從地圖中新增和讀取值

向映射添加值是透過使用大括號 [] 和大括號來完成的,就像數組或切片一樣。在此範例中,我們將建立一個映射,其中鍵為字串,值為整數,用於儲存姓名和年齡。

ages := make(map[string]int)

ages["John"] = 33
ages["Charly"] = 27
ages["Jenny"] = 45
ages["Lisa"] = 19

如果我們想在聲明映射時向其中添加值,我們可以使用簡短聲明並在同一步驟中完成所有操作:

ages := map[string]int{"John": 33, "Charly": 27, "Jenny": 45, "Lisa": 19}

要讀取值,我們只需指示映射的鍵,它將傳回該值。例如,要找出麗莎的年齡,我們可以這樣做:

fmt.Println(ages["Lisa"]) // 19

如果我們嘗試存取不存在的鍵,則獲得的值將是該類型的零值,在本例中它將是“”,因為它是一個字串。

為了檢查映射中是否存在某個元素,我們可以檢查該類型是否為預設類型,但這不是很可靠,因為它可能存在,但其值為空字串,或在int 的情況下為0 ,這將與其零值匹配,因此Go 可以幫助我們完成以下操作:

val, ok := ages["Randy"]

如果我們將映射等於兩個值,第一個將是透過鍵存取的該元素的值,在本例中為“Randy”,它不存在,第二個將是一個布林值,它將指示它是否存在存在與否。

如果我們對值不感興趣,只是想檢查某個鍵是否存在,我們可以使用 _ 來忽略該值,如下所示:

_, ok := ages["Randy"]

與陣列和切片一樣,我們可以使用 len 函數來找出映射中有多少個元素。

fmt.Println(len(ages)) // 4

如果我們想要修改一個值,就像使用一個鍵存取該值並將其與另一個值相符一樣簡單,它就會被修改。

如果我們聲明第二個映射指向第一個映射,如果我們修改第二個映射的值,因為它是引用類型,我們將修改第一個映射的值,因為兩者在下面共享相同的哈希表。

ages := map[string]int{"John": 33, "Charly": 27, "Jenny": 45, "Lisa": 19}
agesNew := ages
agesNew["Bryan"] = 77
fmt.Println(agesNew) // map[Bryan:77 Charly:27 Jenny:45 John:33 Lisa:19]
fmt.Println(ages) // map[Bryan:77 Charly:27 Jenny:45 John:33 Lisa:19]

從地圖中刪除值

為了從映射中刪除元素,Go 為我們提供了一個刪除函數,其簽章如下:delete(m map[Type]Type1, key Type),它接收一個映射和要刪除的鍵。
在前面的例子中,如果我們想消除“Lisa”,我們會這樣做:

delete(ages, "Lisa")

循環遍歷地圖

如果我們想要瀏覽地圖的內容,我們可以使用 for 和我們在數組和切片的帖子中已經看到的範圍變化來完成。
這樣,第一個元素將是索引,因此是鍵,第二個元素是值。

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

// Output:
// Jenny: 45
// Lisa: 19
// John: 33
// Charly: 27

與陣列和切片一樣,如果我們只對值感興趣,而不對鍵感興趣,我們可以使用_省略它。

for _, value := range ages {
    fmt.Println(value)
}

// Output:
// 19
// 33
// 27
// 45

如果我們感興趣的只是鍵,我們可以將範圍分配給單一變數來取得它:

for key := range ages {
    fmt.Println(key)
}

// Output:
// John
// Charly
// Jenny
// Lisa

Sort a map

As I mentioned in the introduction, in a map the information is not ordered, so when looping through it we cannot specify what order it follows, nor can Go guarantee that the order between executions is the same.
As we saw with arrays and slices, in the standard library there is a sort package which helps us sort elements: https://pkg.go.dev/sort

Following our example with ages and using sort, we can sort the keys of the map before traversing it and thus guarantee that it will be accessed in order.

ages := map[string]int{"John": 33, "Charly": 27, "Jenny": 45, "Lisa": 19}
keys := make([]string, 0, len(ages))
for k := range ages {
    keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
    fmt.Println(k, ages[k])
}

// Output:
// Charly 27
// Jenny 45
// John 33
// Lisa 19

We declare our ages map with the short declaration as we saw before.
We create a string slices to store the keys and use the make method with 0 length, since we do not have any keys at the moment, but we do reserve the capacity it will have using the len method for the length of our map.
We go through the ages map to keep its keys and add them to the created slice.
We sort the keys alphabetically with the sort.Strings function.
We go through the slice of keys, already ordered, and access the map with the key in question.
This way we will access the map in an orderly manner and we can do the logic that our program needs.

Problems with Concurrency

Something to keep in mind with maps is that they are not safe to use concurrently. If these are concurrent reads, either accessing a value or through a for with a range, there is no problem with multiple goroutines accessing it at the same time.
The problematic case is when you want to update the value of a map concurrently, either by adding or removing elements from it, and at the same time you are reading it from another side, for example.
To solve this situation there are several possible solutions, which I will not go into much detail, I will simply mention and leave it to your choice to delve deeper into them.

If we use the sync package: https://pkg.go.dev/sync from the standard library, we can control the synchrony between the different goroutines.
A possible use is the RWMutex type which allows us to lock and unlock reads and writes to a type. So if we have a type that contains a sync.RWMutex and a map we can control when it can be accessed.
Another interesting type to investigate within the same sync package is Map, which already offers us a series of functions that will help us work with our map, which in the end we will not be able to work with natively, as with the previous solution.
Depending on the use case we are implementing, one or the other will be more useful to us, and there is no one better than the other, it will always depend on what we need.

I hope everything that I have tried to explain in this post has been clear, and please if there is any part that has not been completely clear or there are parts that I have not covered that you would like me to do, leave me a comment right here or through my social networks that you have on my profile and I will be happy to respond.

以上是Go 中的地圖的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn