網(wǎng)上招聘網(wǎng)站開發(fā)報告中國企業(yè)網(wǎng)
Map
Go語言中提供的映射關(guān)系容器為map
,其內(nèi)部使用散列表(hash)
實現(xiàn)
map是一種無序的基于key-value
的數(shù)據(jù)結(jié)構(gòu),Go語言中的map是引用類型,必須初始化才能使用。
它提供了高效的查找、插入和刪除操作,非常適合用于構(gòu)建關(guān)聯(lián)數(shù)組或字典。
Map 的基本概念
每個 map 都是一個無序的集合,通過鍵來唯一標(biāo)識值。
鍵可以是任何可以比較的類型(如字符串、整數(shù)、結(jié)構(gòu)體等),而值可以是任何類型。
map 是引用類型,因此在傳遞給函數(shù)時,函數(shù)接收到的是指向同一底層數(shù)據(jù)結(jié)構(gòu)的引用。
Go 中的 map 實現(xiàn)細(xì)節(jié)
Go 的 map 是基于哈希表實現(xiàn)的,具有高效的查找、插入和刪除性能。
底層實現(xiàn)使用桶來存儲多個鍵值對,并通過鏈表解決哈希沖突問題。
map 具有自動擴(kuò)容的特性,以保持性能和存儲效率。
哈希函數(shù):每個鍵在插入時會被傳入一個哈希函數(shù),該函數(shù)會生成一個哈希值。這個哈希值用于確定在底層數(shù)組中的位置。
Go 使用的是非加密的哈希函數(shù),針對不同類型(如字符串、整數(shù)等)具有不同的哈希算法。桶(Bucket):在 Go 的 map 中,底層數(shù)組被稱為桶(bucket)。每個桶可以存儲多個鍵值對,這解決了哈希沖突的問題。
當(dāng)兩個或多個鍵經(jīng)過哈希運(yùn)算后映射到同一個桶時,Go 會使用鏈表的方式將它們鏈接在該桶中。
每個桶的大小和數(shù)量會根據(jù)存儲的鍵值對數(shù)量而動態(tài)擴(kuò)展。擴(kuò)容:當(dāng) map 中的元素數(shù)量超過一定閾值時,Go 會對 map 進(jìn)行擴(kuò)容。
擴(kuò)容時,Go 將創(chuàng)建一個新的、更大的底層數(shù)組,并重新計算每個鍵的哈希值以確定新的位置,
然后將現(xiàn)有的鍵值對復(fù)制到新的桶中。
這種擴(kuò)容策略旨在保持哈希表操作的效率,同時降低碰撞的概率。負(fù)載因子:負(fù)載因子是一個衡量哈希表是否需要擴(kuò)展的指標(biāo),通常表示為元素數(shù)量與桶的數(shù)量之比。
在 Go 中,當(dāng)負(fù)載因子超過某個閾值時,會觸發(fā)擴(kuò)容。
創(chuàng)建 Map
可以使用 make 函數(shù)或字面量來創(chuàng)建 map。
package mainimport "fmt"func main() {// 創(chuàng)建一個空的 map,鍵為字符串,值為整數(shù)ageMap := make(map[string]int)// 插入鍵值對ageMap["Alice"] = 25ageMap["Bob"] = 30fmt.Println("年齡Map:", ageMap) // 輸出: 年齡Map: map[Alice:25 Bob:30]
}
package mainimport "fmt"func main() {// 使用字面量創(chuàng)建 mapfruits := map[string]int{"蘋果": 10,"香蕉": 20,"橙子": 15,}fmt.Println("水果數(shù)量:", fruits) // 輸出: 水果數(shù)量: map[香蕉:20 橙子:15 蘋果:10]
}
訪問和修改 Map
使用鍵來訪問或修改對應(yīng)的值。
package mainimport "fmt"func main() {fruits := map[string]int{"蘋果": 10,"香蕉": 20,}// 訪問值appleCount := fruits["蘋果"]fmt.Println("蘋果的數(shù)量:", appleCount) // 輸出: 蘋果的數(shù)量: 10// 修改值fruits["蘋果"] = 12fmt.Println("更新后的蘋果數(shù)量:", fruits["蘋果"]) // 輸出: 更新后的蘋果數(shù)量: 12// 檢查某個鍵是否存在if count, exists := fruits["橙子"]; exists {fmt.Println("橙子的數(shù)量:", count)} else {fmt.Println("橙子不存在")}
}
判斷鍵是否存在
Go語言中有個判斷map中鍵是否存在的特殊寫法,格式如下
value, ok := map[key]
func main() {scoreMap := make(map[string]int)scoreMap["張三"] = 90scoreMap["小明"] = 100// 如果key存在ok為true,v為對應(yīng)的值;不存在ok為false,v為值類型的零值v, ok := scoreMap["張三"]if ok {fmt.Println(v)} else {fmt.Println("查無此人")}
}
刪除 Map 中的元素
可以使用 delete 函數(shù)刪除某個鍵及其對應(yīng)的值。
使用delete()內(nèi)建函數(shù)從map中刪除一組鍵值對,delete()函數(shù)的格式如下:
delete(map, key)
- map:表示要刪除鍵值對的map
- key:表示要刪除的鍵值對的鍵
如果刪除的鍵不存在,則delete()函數(shù)不會報錯。
package mainimport "fmt"func main() {fruits := map[string]int{"蘋果": 10,"香蕉": 20,}// 刪除香蕉delete(fruits, "香蕉")fmt.Println("刪除后的水果數(shù)量:", fruits) // 輸出: 刪除后的水果數(shù)量: map[蘋果:10]
}
遍歷 Map
可以使用 for range 循環(huán)遍歷 map 的所有鍵值對。
遍歷map時的元素順序與添加鍵值對的順序無關(guān)。
package mainimport "fmt"func main() {fruits := map[string]int{"蘋果": 10,"香蕉": 20,"橙子": 15,}// 遍歷 mapfor fruit, count := range fruits {fmt.Printf("%s 的數(shù)量是: %d\n", fruit, count)}
}//只遍歷key
for key := range fruits {fmt.Println(key)
}//只遍歷value
for _, value := range fruits {fmt.Println(value)
}
按照指定順序遍歷map
- 將 map 中的鍵提取到切片中。
- 對切片進(jìn)行排序。
- 按照排序后的鍵順序遍歷 map。
邏輯步驟概述
提取鍵:我們使用 for key := range fruits 迭代 map 的鍵,并將這些鍵添加到切片 keys 中。
排序:使用 sort.Strings(keys) 對鍵進(jìn)行排序。根據(jù)具體的鍵類型(如 int、string、float 等),可以使用適當(dāng)?shù)呐判蚝瘮?shù)。
遍歷:最后,我們遍歷排序后的切片,并使用它來訪問 map 中的值,按照特定順序輸出結(jié)果。
package mainimport ("fmt""sort"
)func main() {// 創(chuàng)建一個 mapfruits := map[string]int{"香蕉": 20,"蘋果": 10,"橙子": 15,"草莓": 25,}// 1. 提取鍵到切片keys := make([]string, 0, len(fruits))for key := range fruits {keys = append(keys, key)}// 2. 對切片進(jìn)行排序sort.Strings(keys)//調(diào)用sort.Strings()函數(shù)對切片進(jìn)行排序,默認(rèn)升序排序// 3. 按照排序后的鍵順序遍歷 mapfor _, key := range keys {fmt.Printf("%s 的數(shù)量是: %d\n", key, fruits[key])}
}蘋果 的數(shù)量是: 10
香蕉 的數(shù)量是: 20
草莓 的數(shù)量是: 25
橙子 的數(shù)量是: 15
元素為map類型的切片
在 Go 語言中,切片的元素可以是任意類型,包括 map 類型。這種靈活性使得可以輕松地組織和管理復(fù)雜的數(shù)據(jù)結(jié)構(gòu)。
當(dāng)需要存儲一組具有相同特征的物體時,可以使用 map 來表示每個物體的屬性,然后將這些 map 存儲在切片中。
邏輯步驟概述
創(chuàng)建切片:我們定義了一個切片 studentGrades,其元素類型為 map[string]int,用來存儲學(xué)生的姓名和成績。添加元素:使用 append() 函數(shù)將新的 map 添加到切片中,每個 map 存儲一個學(xué)生的成績。遍歷切片:通過兩層 for 循環(huán),遍歷切片中的每個 map,并輸出學(xué)生的姓名和成績。
package mainimport "fmt"func main() {// 創(chuàng)建一個切片,元素類型為 map[string]intvar studentGrades []map[string]int// 添加學(xué)生的成績到切片中studentGrades = append(studentGrades, map[string]int{"Alice": 85,"Bob": 90,})studentGrades = append(studentGrades, map[string]int{"Charlie": 70,"David": 95,})// 遍歷切片中的 mapfor i, grades := range studentGrades {fmt.Printf("學(xué)生 %d 的成績:\n", i+1)for name, grade := range grades {fmt.Printf(" %s: %d\n", name, grade)}}
}學(xué)生 1 的成績:Alice: 85Bob: 90
學(xué)生 2 的成績:Charlie: 70David: 95
可以根據(jù)需要使用不同類型的 map,例如:map[string]string:用于存儲鍵為字符串,值也為字符串的映射關(guān)系,如存儲用戶信息或配置項。嵌套結(jié)構(gòu):可以將更復(fù)雜的結(jié)構(gòu)體作為值。type Student struct {Name stringGrade int
}var students []map[string]Studentstudents = append(students, map[string]Student{"Alice": {Name: "Alice", Grade: 85},"Bob": {Name: "Bob", Grade: 90},})
值為切片類型的map
在 Go 語言中,您可以創(chuàng)建一個 map,其值類型為切片(slice)。
這種數(shù)據(jù)結(jié)構(gòu)非常適用于需要將多個值與某個鍵關(guān)聯(lián)的場景,
在 Go 語言中,使用值為切片類型的 map 允許您輕松管理多個值與對應(yīng)鍵的關(guān)系。
這種靈活性使得可以滿足復(fù)雜的數(shù)據(jù)結(jié)構(gòu)需求,便捷地組織和訪問數(shù)據(jù)。
在實際開發(fā)中,可用于各種場景,例如學(xué)生管理、產(chǎn)品信息處理等。
比如將多個學(xué)生的成績與他們的名字關(guān)聯(lián),或是將多個訂單的項目與一個用戶關(guān)聯(lián)。
邏輯步驟概述
創(chuàng)建 map:初始化一個 map,鍵為 string,值為 []int(整型切片)。添加數(shù)據(jù):向 map 中添加鍵值對,其中值是一個切片,存儲多個成績。遍歷 map:通過 for range 遍歷 map,并對每個鍵值的切片進(jìn)行進(jìn)一步遍歷,輸出學(xué)生的姓名及其成績。更新成績:使用 append() 函數(shù)向某個學(xué)生的成績切片中添加新的成績,實現(xiàn)動態(tài)更新。
package mainimport "fmt"func main() {// 創(chuàng)建一個 map,鍵為字符串,值為整數(shù)切片scores := make(map[string][]int)// 向 map 中添加數(shù)據(jù)scores["Alice"] = []int{85, 90, 92}scores["Bob"] = []int{78, 82}scores["Charlie"] = []int{88, 91, 85}// 遍歷 mapfor name, scoreList := range scores {fmt.Printf("%s 的成績: ", name)for _, score := range scoreList {fmt.Printf("%d ", score)}fmt.Println() // 換行}// 示例:給某個學(xué)生添加一個成績scores["Alice"] = append(scores["Alice"], 95)fmt.Printf("更新后 Alice 的成績: %v\n", scores["Alice"])
}
Alice 的成績: 85 90 92
Bob 的成績: 78 82
Charlie 的成績: 88 91 85
更新后 Alice 的成績: [85 90 92 95]
其他用法
可以根據(jù)需要將值的類型定義為其他類型的切片,
如 map[string][]string 用于存儲學(xué)生的課程列表,
或 map[string][]float64 用于存儲某個產(chǎn)品的價格歷史等。
courses := make(map[string][]string)
courses["Alice"] = []string{"數(shù)學(xué)", "英語", "科學(xué)"}
courses["Bob"] = []string{"藝術(shù)", "體育"}for student, courseList := range courses {fmt.Printf("%s 的課程: %v\n", student, courseList)
}prices := make(map[string][]float64)
prices["蘋果"] = []float64{1.5, 2.0, 2.5}
prices["香蕉"] = []float64{1.8, 2.2, 2.6}for fruit, priceList := range prices {fmt.Printf("%s 的價格: ", fruit)for _, price := range priceList {fmt.Printf("%.2f ", price)}fmt.Println() // 換行
}
注意事項
無序性:map 的元素順序是無序的。在遍歷時,輸出的順序并不一定與插入的順序相同。
引用類型:map 是引用類型,傳遞給函數(shù)時傳遞的是引用,因此對 map 的修改會影響到原始數(shù)據(jù)。
零值:如果嘗試訪問一個不存在的鍵,Go 會返回該值類型的零值。在整型 map 中,未存在的鍵返回 0。
并發(fā)安全:map 在并發(fā)環(huán)境下不安全,多個 goroutine 同時讀寫 map 可能會導(dǎo)致程序崩潰??墒褂?sync.Mutex 或其他機(jī)制來實現(xiàn)并發(fā)安全訪問。