Go 语言集合(Map)
映射(Map)是 Go 语言中的关联数组,用于存储键值对。它提供了快速的查找、插入和删除操作,是构建高效程序的重要数据结构。
📋 映射基础
映射声明和初始化
go
package main
import "fmt"
func main() {
// 1. 声明映射(零值为 nil)
var m1 map[string]int
fmt.Printf("零值映射: %v, 是否为nil: %t\n", m1, m1 == nil)
// 2. 使用 make 创建映射
m2 := make(map[string]int)
fmt.Printf("make映射: %v, 长度: %d\n", m2, len(m2))
// 3. 映射字面量初始化
m3 := map[string]int{
"apple": 5,
"banana": 3,
"orange": 8,
}
fmt.Printf("字面量映射: %v, 长度: %d\n", m3, len(m3))
// 4. 空映射初始化
m4 := map[string]int{}
fmt.Printf("空映射: %v, 长度: %d\n", m4, len(m4))
// 5. 不同类型的映射
ages := map[string]int{
"Alice": 25,
"Bob": 30,
"Carol": 28,
}
scores := map[int]float64{
1001: 95.5,
1002: 87.3,
1003: 92.1,
}
fmt.Printf("年龄映射: %v\n", ages)
fmt.Printf("分数映射: %v\n", scores)
}映射操作
go
func main() {
// 创建映射
fruits := make(map[string]int)
// 添加元素
fruits["apple"] = 10
fruits["banana"] = 5
fruits["orange"] = 8
fmt.Printf("初始映射: %v\n", fruits)
// 访问元素
appleCount := fruits["apple"]
fmt.Printf("苹果数量: %d\n", appleCount)
// 修改元素
fruits["apple"] = 15
fmt.Printf("修改后: %v\n", fruits)
// 检查键是否存在
count, exists := fruits["grape"]
if exists {
fmt.Printf("葡萄数量: %d\n", count)
} else {
fmt.Printf("葡萄不存在,返回零值: %d\n", count)
}
// 添加新元素
fruits["grape"] = 12
fmt.Printf("添加葡萄后: %v\n", fruits)
// 删除元素
delete(fruits, "banana")
fmt.Printf("删除香蕉后: %v\n", fruits)
// 映射长度
fmt.Printf("映射长度: %d\n", len(fruits))
}🔍 映射遍历
遍历键值对
go
func main() {
studentGrades := map[string]float64{
"Alice": 95.5,
"Bob": 87.3,
"Carol": 92.1,
"David": 78.9,
"Eve": 88.7,
}
fmt.Println("遍历所有键值对:")
for name, grade := range studentGrades {
fmt.Printf("学生: %s, 成绩: %.1f\n", name, grade)
}
fmt.Println("\n只遍历键:")
for name := range studentGrades {
fmt.Printf("学生: %s\n", name)
}
fmt.Println("\n只遍历值:")
for _, grade := range studentGrades {
fmt.Printf("成绩: %.1f\n", grade)
}
// 注意:映射的遍历顺序是随机的
fmt.Println("\n多次遍历展示随机顺序:")
for i := 0; i < 3; i++ {
fmt.Printf("第%d次遍历: ", i+1)
for name := range studentGrades {
fmt.Printf("%s ", name)
}
fmt.Println()
}
}有序遍历
go
import (
"fmt"
"sort"
)
func main() {
data := map[string]int{
"charlie": 3,
"alice": 1,
"bob": 2,
"dave": 4,
}
fmt.Printf("原始映射: %v\n", data)
// 按键排序遍历
var keys []string
for key := range data {
keys = append(keys, key)
}
sort.Strings(keys)
fmt.Println("按键排序遍历:")
for _, key := range keys {
fmt.Printf("%s: %d\n", key, data[key])
}
// 按值排序遍历
type KeyValue struct {
Key string
Value int
}
var kvPairs []KeyValue
for key, value := range data {
kvPairs = append(kvPairs, KeyValue{key, value})
}
// 按值排序
sort.Slice(kvPairs, func(i, j int) bool {
return kvPairs[i].Value < kvPairs[j].Value
})
fmt.Println("按值排序遍历:")
for _, kv := range kvPairs {
fmt.Printf("%s: %d\n", kv.Key, kv.Value)
}
}🔧 高级映射操作
映射的映射
go
func main() {
// 嵌套映射:学生的各科成绩
studentScores := map[string]map[string]float64{
"Alice": {
"Math": 95.0,
"English": 88.5,
"Science": 92.3,
},
"Bob": {
"Math": 87.5,
"English": 91.2,
"Science": 89.8,
},
}
fmt.Println("学生成绩:")
for student, scores := range studentScores {
fmt.Printf("%s的成绩:\n", student)
for subject, score := range scores {
fmt.Printf(" %s: %.1f\n", subject, score)
}
}
// 添加新学生
studentScores["Carol"] = make(map[string]float64)
studentScores["Carol"]["Math"] = 93.7
studentScores["Carol"]["English"] = 89.4
studentScores["Carol"]["Science"] = 95.1
// 修改成绩
studentScores["Alice"]["Math"] = 97.0
// 安全访问嵌套映射
if aliceScores, exists := studentScores["Alice"]; exists {
if mathScore, hasMatch := aliceScores["Math"]; hasMatch {
fmt.Printf("Alice的数学成绩: %.1f\n", mathScore)
}
}
// 计算平均分
for student, scores := range studentScores {
total := 0.0
count := 0
for _, score := range scores {
total += score
count++
}
average := total / float64(count)
fmt.Printf("%s的平均分: %.2f\n", student, average)
}
}映射作为集合
go
func main() {
// 使用映射实现集合功能
set1 := make(map[string]bool)
set2 := make(map[string]bool)
// 添加元素到集合
fruits1 := []string{"apple", "banana", "orange", "grape"}
fruits2 := []string{"banana", "orange", "kiwi", "mango"}
for _, fruit := range fruits1 {
set1[fruit] = true
}
for _, fruit := range fruits2 {
set2[fruit] = true
}
fmt.Printf("集合1: %v\n", getKeys(set1))
fmt.Printf("集合2: %v\n", getKeys(set2))
// 交集
intersection := make(map[string]bool)
for key := range set1 {
if set2[key] {
intersection[key] = true
}
}
fmt.Printf("交集: %v\n", getKeys(intersection))
// 并集
union := make(map[string]bool)
for key := range set1 {
union[key] = true
}
for key := range set2 {
union[key] = true
}
fmt.Printf("并集: %v\n", getKeys(union))
// 差集 (set1 - set2)
difference := make(map[string]bool)
for key := range set1 {
if !set2[key] {
difference[key] = true
}
}
fmt.Printf("差集(1-2): %v\n", getKeys(difference))
}
// 获取映射的所有键
func getKeys(m map[string]bool) []string {
keys := make([]string, 0, len(m))
for key := range m {
keys = append(keys, key)
}
return keys
}映射的复制和比较
go
func main() {
original := map[string]int{
"a": 1,
"b": 2,
"c": 3,
}
// 浅复制
shallowCopy := original
fmt.Printf("原始映射: %v\n", original)
fmt.Printf("浅复制: %v\n", shallowCopy)
// 修改浅复制会影响原始映射
shallowCopy["d"] = 4
fmt.Printf("修改浅复制后:\n")
fmt.Printf("原始映射: %v\n", original)
fmt.Printf("浅复制: %v\n", shallowCopy)
// 深复制
deepCopy := make(map[string]int)
for key, value := range original {
deepCopy[key] = value
}
fmt.Printf("深复制: %v\n", deepCopy)
// 修改深复制不会影响原始映射
deepCopy["e"] = 5
fmt.Printf("修改深复制后:\n")
fmt.Printf("原始映射: %v\n", original)
fmt.Printf("深复制: %v\n", deepCopy)
// 映射比较(只能与 nil 比较)
var nilMap map[string]int
fmt.Printf("原始映射是否为nil: %t\n", original == nil)
fmt.Printf("nil映射是否为nil: %t\n", nilMap == nil)
// 自定义映射比较函数
map1 := map[string]int{"a": 1, "b": 2}
map2 := map[string]int{"a": 1, "b": 2}
map3 := map[string]int{"a": 1, "b": 3}
fmt.Printf("map1 == map2: %t\n", compareMaps(map1, map2))
fmt.Printf("map1 == map3: %t\n", compareMaps(map1, map3))
}
// 自定义映射比较函数
func compareMaps(m1, m2 map[string]int) bool {
if len(m1) != len(m2) {
return false
}
for key, value1 := range m1 {
if value2, exists := m2[key]; !exists || value1 != value2 {
return false
}
}
return true
}🎯 实际应用示例
单词统计器
go
import (
"fmt"
"regexp"
"sort"
"strings"
)
type WordCounter struct {
words map[string]int
total int
}
func NewWordCounter() *WordCounter {
return &WordCounter{
words: make(map[string]int),
total: 0,
}
}
func (wc *WordCounter) AddText(text string) {
// 转换为小写并提取单词
text = strings.ToLower(text)
reg := regexp.MustCompile(`[a-z]+`)
words := reg.FindAllString(text, -1)
for _, word := range words {
wc.words[word]++
wc.total++
}
}
func (wc *WordCounter) GetWordCount(word string) int {
return wc.words[strings.ToLower(word)]
}
func (wc *WordCounter) GetTotalWords() int {
return wc.total
}
func (wc *WordCounter) GetUniqueWords() int {
return len(wc.words)
}
func (wc *WordCounter) GetTopWords(n int) []struct {
Word string
Count int
} {
type WordCount struct {
Word string
Count int
}
var wordCounts []WordCount
for word, count := range wc.words {
wordCounts = append(wordCounts, WordCount{word, count})
}
// 按计数排序
sort.Slice(wordCounts, func(i, j int) bool {
return wordCounts[i].Count > wordCounts[j].Count
})
if n > len(wordCounts) {
n = len(wordCounts)
}
return wordCounts[:n]
}
func (wc *WordCounter) GetWordFrequency(word string) float64 {
count := wc.GetWordCount(word)
if wc.total == 0 {
return 0
}
return float64(count) / float64(wc.total) * 100
}
func main() {
counter := NewWordCounter()
// 添加文本
texts := []string{
"Go is a programming language developed by Google.",
"Go is simple, fast, and reliable.",
"Programming in Go is fun and productive.",
"Google created Go for modern software development.",
}
for _, text := range texts {
counter.AddText(text)
}
// 统计结果
fmt.Printf("总词数: %d\n", counter.GetTotalWords())
fmt.Printf("唯一词数: %d\n", counter.GetUniqueWords())
// 查询特定单词
queryWords := []string{"go", "programming", "google", "python"}
fmt.Println("\n单词查询:")
for _, word := range queryWords {
count := counter.GetWordCount(word)
frequency := counter.GetWordFrequency(word)
fmt.Printf("%s: 出现%d次, 频率%.2f%%\n", word, count, frequency)
}
// 高频词汇
fmt.Println("\n前5个高频词汇:")
topWords := counter.GetTopWords(5)
for i, wc := range topWords {
fmt.Printf("%d. %s: %d次\n", i+1, wc.Word, wc.Count)
}
}缓存系统
go
import (
"fmt"
"sync"
"time"
)
// 缓存项
type CacheItem struct {
Value interface{}
Expiration time.Time
}
// 检查是否过期
func (item CacheItem) IsExpired() bool {
return time.Now().After(item.Expiration)
}
// 简单缓存系统
type SimpleCache struct {
items map[string]CacheItem
mutex sync.RWMutex
}
func NewSimpleCache() *SimpleCache {
cache := &SimpleCache{
items: make(map[string]CacheItem),
}
// 启动清理过期项的 goroutine
go cache.cleanupExpired()
return cache
}
// 设置缓存项
func (c *SimpleCache) Set(key string, value interface{}, duration time.Duration) {
c.mutex.Lock()
defer c.mutex.Unlock()
expiration := time.Now().Add(duration)
c.items[key] = CacheItem{
Value: value,
Expiration: expiration,
}
}
// 获取缓存项
func (c *SimpleCache) Get(key string) (interface{}, bool) {
c.mutex.RLock()
defer c.mutex.RUnlock()
item, exists := c.items[key]
if !exists {
return nil, false
}
if item.IsExpired() {
delete(c.items, key)
return nil, false
}
return item.Value, true
}
// 删除缓存项
func (c *SimpleCache) Delete(key string) {
c.mutex.Lock()
defer c.mutex.Unlock()
delete(c.items, key)
}
// 获取缓存大小
func (c *SimpleCache) Size() int {
c.mutex.RLock()
defer c.mutex.RUnlock()
return len(c.items)
}
// 清空缓存
func (c *SimpleCache) Clear() {
c.mutex.Lock()
defer c.mutex.Unlock()
c.items = make(map[string]CacheItem)
}
// 获取所有键
func (c *SimpleCache) Keys() []string {
c.mutex.RLock()
defer c.mutex.RUnlock()
keys := make([]string, 0, len(c.items))
for key, item := range c.items {
if !item.IsExpired() {
keys = append(keys, key)
}
}
return keys
}
// 清理过期项
func (c *SimpleCache) cleanupExpired() {
ticker := time.NewTicker(1 * time.Minute)
defer ticker.Stop()
for range ticker.C {
c.mutex.Lock()
for key, item := range c.items {
if item.IsExpired() {
delete(c.items, key)
}
}
c.mutex.Unlock()
}
}
func main() {
cache := NewSimpleCache()
// 设置缓存项
cache.Set("user:1001", "Alice", 5*time.Second)
cache.Set("user:1002", "Bob", 10*time.Second)
cache.Set("config:timeout", 30, 1*time.Minute)
fmt.Printf("缓存大小: %d\n", cache.Size())
fmt.Printf("所有键: %v\n", cache.Keys())
// 获取缓存项
if value, exists := cache.Get("user:1001"); exists {
fmt.Printf("用户1001: %v\n", value)
}
if value, exists := cache.Get("config:timeout"); exists {
fmt.Printf("超时配置: %v\n", value)
}
// 等待一些项过期
fmt.Println("等待5秒...")
time.Sleep(6 * time.Second)
fmt.Printf("5秒后缓存大小: %d\n", cache.Size())
fmt.Printf("5秒后所有键: %v\n", cache.Keys())
// 再次获取过期的项
if value, exists := cache.Get("user:1001"); exists {
fmt.Printf("用户1001: %v\n", value)
} else {
fmt.Println("用户1001已过期")
}
if value, exists := cache.Get("user:1002"); exists {
fmt.Printf("用户1002: %v\n", value)
} else {
fmt.Println("用户1002已过期")
}
}🎓 小结
本章我们全面学习了 Go 语言的映射:
- ✅ 映射基础:声明、初始化、基本操作
- ✅ 映射遍历:随机遍历、有序遍历
- ✅ 高级操作:嵌套映射、映射作为集合
- ✅ 实际应用:单词统计器、缓存系统
映射是 Go 语言中重要的数据结构,掌握映射的使用对于构建高效的程序至关重要。
接下来,我们将学习 Go 语言接口,了解 Go 语言的多态和抽象机制。
映射使用建议
- 映射不是线程安全的,并发访问需要加锁
- 映射的遍历顺序是随机的,不要依赖顺序
- 检查键是否存在时使用
value, ok := map[key]语法 - 大量数据时考虑映射的内存占用