首頁 > 軟體

Go語言對映內部實現及基礎功能實戰

2022-03-21 22:00:06

寫在前面

嗯,學習GO,所以有了這篇文章博文內容為《GO語言實戰》讀書筆記之一主要涉及對映相關知識

你要愛就要像一個痴情的戀人那樣去愛,像一個忘死的夢者那樣去愛,視他人之疑目如盞盞鬼火,大膽去走你的夜路。——史鐵生《病隙碎筆》

對映的內部實現和基礎功能

對映是一種資料結構,是用於儲存一系列無序的鍵值對。類比Java裡的Map,Python裡的字典,可以理解為以雜湊值做索引,期望索引可以在一定的連續記憶體範圍內的類似陣列的資料結構。

對映裡基於鍵來儲存值。對映功能強大的地方是,能夠基於鍵快速檢索資料。鍵就像索引一樣,指向與該鍵關聯的值。

內部實現

對映是一個集合,可以使用類似處理陣列和切片的方式迭代對映中的元素。但對映是無序的集合,無序的原因是對映的實現使用了雜湊表.

對映的雜湊表包含一組桶。

在儲存、刪除或者查詢鍵值對的時候,所有操作都要先選擇一個桶。把操作對映時指定的鍵傳給對映的雜湊函數,就能選中對應的桶。

這個雜湊函數的目的是生成一個索引,這個索引最終將鍵值對分佈到所有可用的桶裡。對 Go 語言的對映來說,生成的雜湊鍵的一部分,具體來說是低位(LOB),被用來選擇桶。

桶的內部實現。對映使用兩個資料結構來儲存資料,

第一個是陣列,內部儲存用於選擇桶的雜湊鍵的高八位值。用於區分每個鍵值對要存在桶裡的那一項。第二個是位元組陣列,用於儲存鍵值對。該位元組陣列先依次儲存了這個桶裡所有的鍵,之後依次儲存了這個桶裡所有的值。實現這種鍵值對的儲存方式目的在於減少每個桶所需的記憶體。

對映儲存的增加,索引分佈越均勻,存取鍵值對的速度就越快,隨著對映儲存的增加,索引分佈越均勻,存取鍵值對的速度就越快。對映通過合理數量的桶來平衡鍵值對的分佈

建立和初始化

Go 語言中有很多種方法可以建立並初始化對映,可以使用內建的make 函數,也可以使用對映字面量。

package main
import (
	"fmt"
)
func main() {
	// 建立一個對映,鍵的型別是 string,值的型別是 int
	dict := make(map[string]int)
	// 建立一個對映,鍵和值的型別都是 string
	// 使用兩個鍵值對初始化對映
	dict_ := map[string]string{"Red": "#da1337", "Orange": "#e95a22"}
	fmt.Println(dict)
	fmt.Print(dict_)
}
======
map[]
map[Orange:#e95a22 Red:#da1337]

建立對映時,更常用的方法是使用對映字面量。對映的初始長度會根據初始化時指定的鍵值對的數量來確定。

對映的鍵可以是任何值。這個值的型別可以是內建的型別,也可以是結構型別,只要這個值可以使用==運運算元做比較

切片、函數以及包含切片的結構型別由於具有參照語意,不能作為對映的鍵,使用這些型別會造成編譯錯誤

package main
import (
	"fmt"
)
func main() {
	// 建立一個對映,使用字串切片作為對映的鍵
	dict := map[[]string]int{}
	fmt.Println(dict)
}
====
[Running] go run "d:GolandProjectscode-masterdemohello.go"
# command-line-arguments
demohello.go:10:45: duplicate key "Red" in map literal
	previous key at demohello.go:10:28
[Done] exited with code=2 in 0.902 seconds

宣告一個儲存字串切片的對映

// 建立一個對映,使用字串切片作為值
dict := map[int][]string{}

使用對映

鍵值對賦值給對映,是通過指定適當型別的鍵並給這個鍵賦一個值來完成的

為對映賦值

// 建立一個空對映,用來儲存顏色以及顏色對應的十六進位製程式碼
colors := map[string]string{}
// 將 Red 的程式碼加入到對映
colors["Red"] = "#da1337"

可以通過宣告一個未初始化的對映來建立一個值為nil的對映,不能用於儲存鍵值對.

// 建立一個空對映,用來儲存顏色以及顏色對應的十六進位製程式碼
colors := map[string]string{}
// 將 Red 的程式碼加入到對映
colors["Red"] = "#da1337"

從對映取值時有兩個選擇:

第一個選擇是,可以同時獲得值,以及一個表示這個鍵是否存在的標誌,

從對映獲取值並判斷鍵是否存在

// 獲取鍵 Blue 對應的值
value := colors["Blue"]
// 這個鍵存在嗎?
if value != "" {
fmt.Println(value)
} 

另一個選擇是,只返回鍵對應的值,然後通過判斷這個值是不是零值來確定鍵是否存在

從對映獲取值,並通過該值判斷鍵是否存在

// 獲取鍵 Blue 對應的值
value := colors["Blue"]
// 這個鍵存在嗎?
if value != "" {
fmt.Println(value)
} 

在Go語言裡,通過鍵來索引對映時,即便這個鍵不存在也總會返回一個值。在這種情況下,返回的是該值對應的型別的零值

迭代對映裡的所有值和迭代陣列或切片一樣,使用關鍵字 range

使用range 迭代對映

// 建立一個對映,儲存顏色以及顏色對應的十六進位製程式碼colors := map[string]string{<!--{C}%3C!%2D%2D%20%2D%2D%3E-->"AliceBlue": "#f0f8ff","Coral": "#ff7F50","DarkGray": "#a9a9a9","ForestGreen": "#228b22",}// 顯示對映裡的所有顏色for key, value := range colors {<!--{C}%3C!%2D%2D%20%2D%2D%3E-->fmt.Printf("Key: %s Value: %sn", key, value)}// 建立一個對映,儲存顏色以及顏色對應的十六進位製程式碼
colors := map[string]string{
"AliceBlue": "#f0f8ff",
"Coral": "#ff7F50",
"DarkGray": "#a9a9a9",
"ForestGreen": "#228b22",
}
// 顯示對映裡的所有顏色
for key, value := range colors {
	fmt.Printf("Key: %s Value: %sn", key, value)
} 

想把一個鍵值對從對映裡刪除,就使用內建的delete 函數

從對映中刪除一項

// 刪除鍵為 Coral 的鍵值對delete(colors, "Coral")// 顯示對映裡的所有顏色for key, value := range colors {<!--{C}%3C!%2D%2D%20%2D%2D%3E-->fmt.Printf("Key: %s Value: %sn", key, value)}// 刪除鍵為 Coral 的鍵值對
delete(colors, "Coral")
// 顯示對映裡的所有顏色
for key, value := range colors {
	fmt.Printf("Key: %s Value: %sn", key, value)
} 

在函數間傳遞對映

在函數間傳遞對映並不會製造出該對映的一個副本。實際上,當傳遞對映給一個函數,並對這個對映做了修改時,所有對這個對映的參照都會察覺到這個修改,這個特性和切片類似,保證可以用很小的成本來複制對映

package mainimport ("fmt")func main() {<!--{C}%3C!%2D%2D%20%2D%2D%3E-->// 建立一個對映,儲存顏色以及顏色對應的十六進位製程式碼colors := map[string]string{<!--{C}%3C!%2D%2D%20%2D%2D%3E-->"AliceBlue": "#f0f8ff","Coral": "#ff7F50","DarkGray": "#a9a9a9","ForestGreen": "#228b22",}// 顯示對映裡的所有顏色for key, value := range colors {<!--{C}%3C!%2D%2D%20%2D%2D%3E-->fmt.Printf("Key: %s Value: %sn", key, value)}fmt.Println("呼叫函數來移除指定的鍵")// 呼叫函數來移除指定的鍵removeColor(colors, "Coral")// 顯示對映裡的所有顏色for key, value := range colors {<!--{C}%3C!%2D%2D%20%2D%2D%3E-->fmt.Printf("Key: %s Value: %sn", key, value)}}// removeColor 將指定對映裡的鍵刪除func removeColor(colors map[string]string, key string) {<!--{C}%3C!%2D%2D%20%2D%2D%3E-->delete(colors, key)}package main

import (
	"fmt"
)
func main() {
	// 建立一個對映,儲存顏色以及顏色對應的十六進位製程式碼
	colors := map[string]string{
		"AliceBlue":   "#f0f8ff",
		"Coral":       "#ff7F50",
		"DarkGray":    "#a9a9a9",
		"ForestGreen": "#228b22",
	}
	// 顯示對映裡的所有顏色
	for key, value := range colors {
		fmt.Printf("Key: %s Value: %sn", key, value)
	}
	fmt.Println("呼叫函數來移除指定的鍵")
	// 呼叫函數來移除指定的鍵
	removeColor(colors, "Coral")
	
	// 顯示對映裡的所有顏色
	for key, value := range colors {
		fmt.Printf("Key: %s Value: %sn", key, value)
	}
}
// removeColor 將指定對映裡的鍵刪除
func removeColor(colors map[string]string, key string) {
	delete(colors, key)
}
[Running] go run "d:GolandProjectscode-masterdemohello.go"
Key: Coral Value: #ff7F50
Key: DarkGray Value: #a9a9a9
Key: ForestGreen Value: #228b22
Key: AliceBlue Value: #f0f8ff
呼叫函數來移除指定的鍵
Key: AliceBlue Value: #f0f8ff
Key: DarkGray Value: #a9a9a9
Key: ForestGreen Value: #228b22
[Done] exited with code=0 in 1.419 seconds

對映的增長沒有容量或者任何限制。同時內建函數 len可以用來獲取切片或者對映的長度。但是內建函數 cap只能用於切片。

以上就是Go語言對映內部實現及基礎功能實戰的詳細內容,更多關於Go語言對映內部實現和基礎功能的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com