1. 程式人生 > >go——字典(二)

go——字典(二)

字典是一種資料結構,用於儲存一系列無序的鍵值對。

字典是基於鍵來儲存值。字典功能強大的地方是能夠基於鍵快速檢索資料。

鍵就像索引一樣,指向與改鍵關聯的值。

1.內部實現

字典是一個集合,可以使用類似處理陣列和切片的方式迭代字典中的元素。

但字典是無序的集合,意味著沒有辦法預測鍵值對被返回的順序。

即使使用同樣的順序儲存鍵值對,每次迭代字典的時候順序也可能不一樣。

無序的原因是字典使用了散列表。

字典的散列表包含一組桶。在儲存、刪除或者查詢鍵值對的時候,所有操作都要先選擇一個桶。

把操作字典時指定的鍵傳給字典的雜湊函式,就能選中對應的桶。

這個雜湊函式的目的是生成一個索引,這個索引最終將鍵值對分佈到所有可用的桶裡。

隨著字典儲存的增加,索引分佈越均勻,訪問鍵值對的速度就越快。

如果你在字典裡儲存了10000個元素,你不希望每次查詢都要訪問10000個鍵值對才能找到需要的元素,你希望查詢鍵值對的次數越少越好。

對於有10000個元素的字典,每次查詢都只需要查詢8個鍵值對才是一個分佈得比較好得字典。

字典通過合理數量的桶來平衡鍵值對得分佈。

鍵會轉化為一個數值(雜湊值)。這個數值落在字典已有桶的序號範圍內表示一個可用於儲存的桶的序號。

之後之歌數值就被用於選擇桶,用於儲存或者查詢指定的鍵值對。

對Go語言的字典來說,生成的雜湊鍵的一部分,具體來說是低位(LOB)被用來選擇桶。

字典使用兩個資料結構來儲存資料。第一個資料結構是一個數組,內部儲存的是用於選擇桶的雜湊鍵的高八位值,

這個數值就被用於選擇桶,用於儲存或者查詢指定的鍵值對。

第二個資料結構是一個位元組陣列,用於儲存鍵值對。該位元組陣列先依次儲存了這個桶裡所有的鍵,之後依次儲存了這個桶裡所有的值。

實現這種鍵值對的儲存方式目的在於減少每個桶所需的記憶體。

 

2.建立和初始化

 (1)使用make宣告字典

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

//建立一個字典,鍵的型別是string,值得型別是int
dict := make(map[string]int)

//建立一個字典,鍵和值得型別都是string
//使用兩個鍵值對初始化字典
dict := map[string]string{"name":"kebi", "sex":"boy"}

(2)使用字典字面量宣告空字典

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

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

切片、函式以及包含切片的結構型別這些型別由於具有引用語義,不能作為字典的鍵,使用這些鍵會造成編譯錯誤。

dict := map[[]string]string{}  //invalid map key type []string

(3)宣告一個儲存字串切片的字典

沒有任何理由阻止使用者使用切片作為字典的值。

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

  

3.使用字典

 (1)為字典複製

鍵值對賦值給字典,是通過指定適當型別的鍵並給這個件賦予一個值來完成的。

//建立一個空字典,用來儲存顏色以及顏色對應的十六進位制程式碼
colors := map[string]string{}

//將Red的程式碼加入到字典
colors["Red"] = "#da1337"

(2)對nil字典賦值時的語言執行時錯誤

可通過宣告一個未初始化的字典來建立一個值為nil的字典。

nil字典不能用於儲存鍵值對,否則,會產生一個語言執行錯誤

//通過宣告字典建立一個nil字典
var colors map[string]string  


//將red的程式碼加入到字典
colors["red"] = "#da1337"  //assignment to entry in nil map

(3)從字典獲取值並判斷鍵是否存在

測試字典裡是否存在某個鍵是字典的一個重要操作。

這樣操作允許使用者寫一些邏輯來確定是否完成某個操作或者是否在字典裡快取了一個特定資料。

這樣操作也可以用來比較兩個字典,來確定那個鍵值對互相匹配,那些鍵值對不匹配。

從字典取值時有兩個選擇。第一個選擇是,可以同時獲得值,以及一個表示這個鍵是否存在的標誌。

//獲取鍵blue對應的值
value, exists := colors["blue"]

//這個鍵存在嗎?
if exists {
    fmt.PrintLn(value)  
}

(4)從字典獲取值,並通過該值判斷鍵是否存在

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

//獲取鍵Blue對應得值
value := colors["blue"]

//這個鍵存在嗎?
if value != " " {
    fmt.PrintLn(value)
} 

在Go語言裡,通過鍵來索引字典時,即便這個鍵不存在也總會返回一個值。

在這種情況下,返回得是該值對應得型別的零值。

(5)使用range迭代字典

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

對字典來說,range返回的不是索引的值,而是鍵值對。

package main

import "fmt"

func main() {
	//建立一個字典,儲存顏色以及顏色對應的十六進位制程式碼
	colors := map[string]string{
		"red":    "#34567",
		"blue":   "#6789",
		"pink":   "#drtuft",
		"yellow": "#df67",
	}
	//顯式所有顏色
	for key, value := range colors {
		fmt.Printf("Key: %s, value: %s\n", key, value)
	}
}

/*
結果
Key: red, value: #34567
Key: blue, value: #6789
Key: pink, value: #drtuft
Key: yellow, value: #df67
*/

(6)從字典中刪除一項

如果想要把一個鍵值從字典裡刪除,就使用內建的delete函式。

//刪除鍵為red的鍵值對
delete(colors, "red)

  

4.在函式間傳遞字典

在函式鍵傳遞字典並不會製造出該字典的一個副本。

實際上,當傳遞一個字典給一個函式,並對這個函式做出修改時,所有對這個字典的引用都會察覺到這個修改。

package main

import "fmt"

func recoveColor(colors map[string]string, key string) {
	delete(colors, key)
}

func main() {
	//建立一個字典,儲存顏色以及顏色對應的十六進位制程式碼
	colors := map[string]string{
		"red":    "#34567",
		"blue":   "#6789",
		"pink":   "#drtuft",
		"yellow": "#df67",
	}
	//顯式所有顏色
	for key, value := range colors {
		fmt.Printf("Key: %s, value: %s\n", key, value)
	}

	//呼叫函式移除指定的鍵
	recoveColor(colors, "red")

	//顯式所有顏色
	fmt.Println(" ")
	for key, value := range colors {
		fmt.Printf("Key: %s, value: %s\n", key, value)
	}
}
/*
直接修改原字典
Key: red, value: #34567
Key: blue, value: #6789
Key: pink, value: #drtuft
Key: yellow, value: #df67
 
Key: yellow, value: #df67
Key: blue, value: #6789
Key: pink, value: #drtuft
*/