史上最易懂的Go语言map完全指南

简介: 史上最易懂的Go语言map完全指南

map 是 Go 语言中一种非常重要的数据结构,它可以存储键值对数据,通过键快速查找值,功能类似于其他语言中的字典或哈希表。正确使用 map 可以写出简洁高效的 Go 语言代码。

本文将全面介绍 Go 语言 map 的相关知识,包括:

  1. map 基本介绍
  2. map 的声明和初始化
  3. map 操作方法
  4. map 遍历
  5. 自定义 map 键类型
  6. 用于字典的 map
  7. map 线程安全性 issue
  8. map 存在的问题
  9. 应用场景

通过学习本文提供的详细内容,你可以深入掌握 Go 语言 map 的各种用法和技巧,更好地在代码中使用 map。


1

 

1. map 基本介绍

map 是一种无序的键值对集合,通过 key 可以快速查找对应的值。

map 使用哈希表实现,lookup 时间复杂度为 O(1)。

map 的 key 可以是任意可比较类型,比如 string、int、float 等,value 可以是任意类型。一个简单的 map 定义:

map[string]int

表示键是 string,值是 int 的 map。

需要注意 map 是引用类型,遵守引用语义。


2

 

2. map 声明和初始化

一个 map 需要先声明才能使用:

// 声明一个键值都是string的map
var m1 map[string]string

只是声明的 map 可以存取,实际还未分配空间,需要使用 make 初始化:

m1 = make(map[string]string)

make 函数会为 map 分配内存空间,可以指定 map 大小,如果不指定则由运行时动态扩容。

也可以在声明时填充元素,语法是:map[keytype]valuetype {key1:value1, key2:value2}:

m2 := map[string]string{"one":"a", "two":"b"}

这样 m2 初始化了两个元素。


3

 

3. map 操作

map 常见操作方法:

m := map[string]int{"one": 1, "two": 2}
len(m) // 元素个数
m["one"] // 读取元素
m["three"] = 3 // 设置新元素
delete(m, "two") // 删除元素 
elem, ok := m["four"] // 测试是否存在

这些操作方法可以完成对 map 的基本增删改查。

需要注意读取一个不存在的键会返回值类型的零值。


4

 

4. map 遍历

可以通过 for-range 遍历 map:

for k, v := range m {
  fmt.Println(k, v)
}

需要注意 map 的遍历顺序是不确定的。

只需要 key 可以:

for k := range m {
  fmt.Println(k)
}

遍历可以删除元素:

for k := range m {
  if meetCondition(k) {
    delete(m, k)
  }
}

这在清理 map 时很有用。


5

 

5. 自定义 map 键类型

map 的键可以是任意可比较类型,除了 slice、map、function 的内建类型,也可以是自定义类型。

但自定义类型作为键要求需要实现==和!=操作,以便比较键是否相等。

例如:

type user struct {
  id int
  name string
}
// 实现Key接口
func (u user) ==(other user) {
  return u.id == other.id
}
m := map[user]string{
  user{1, "a"}:"foo",
}

通过实现 Key 接口,user 可以作为 map 的键使用。


6

 

6. map 用于字典

map 非常适合用来表示字典:

dict := map[string]string{
  "apple": "苹果",
  "banana": "香蕉",
}
dict["apple"] // 苹果

可以快速通过单词查找对应的翻译。

可以用 slice 存储字典顺序:

// 字典顺序
keys := []string{"apple", "banana"}  
// 查单词时使用
dict[keys[0]]

这样可以实现一个基本的字典结构。


7

 

7. map 线程安全性

需要注意的是,map 在多线程环境下访问时不是线程安全的。

以下操作会引发竞态条件:

  • 读写 map 的同时写 map
  • 读写 map 的同时循环遍历读 map
  • 循环遍历读 map 的同时写 map

这会导致 panic 异常。

可以通过 sync.RWMutex 来保证互斥访问:

var m = struct {
  sync.RWMutex 
  data map[string]string
}{data: make(map[string]string)}
// 读锁
m.RLock()  
elem := m.data["key"]
m.RUnlock()
// 写锁
m.Lock()
m.data["key"] = "value"
m.Unlock()

8

 

8. map 存在的问题

map 是一个非常有用的数据结构,但是也存在一些问题需要注意:

  • 线程不安全,需要外部同步
  • 元素顺序随机,不能保证遍历顺序
  • 储存空间较大,内存占用高
  • 元素不能按照插入时间排序
  • 空间不会自动缩容

这需要根据使用场景权衡选择是否使用 map。在需要顺序或稳定遍历时不能使用 map。


9

 

9. map 使用场景

map 经常在以下场景中使用:

  • 字典翻译
  • 查找表
  • 缓存
  • 集合与并集
  • 频率统计
  • 元素计数


10

 

总结

本文详细介绍了 Go 语言 map 的重要知识点,包括声明、初始化、操作、遍历、自定义键等,也列出了 map 的使用场景和需要注意的问题。掌握这些可以使我们更好地理解和运用 map,编写出简洁高效的 Go 语言代码。



目录
相关文章
|
6天前
|
存储 JSON 监控
Viper,一个Go语言配置管理神器!
Viper 是一个功能强大的 Go 语言配置管理库,支持从多种来源读取配置,包括文件、环境变量、远程配置中心等。本文详细介绍了 Viper 的核心特性和使用方法,包括从本地 YAML 文件和 Consul 远程配置中心读取配置的示例。Viper 的多来源配置、动态配置和轻松集成特性使其成为管理复杂应用配置的理想选择。
23 2
|
10天前
|
JavaScript Java Go
探索Go语言在微服务架构中的优势
在微服务架构的浪潮中,Go语言以其简洁、高效和并发处理能力脱颖而出。本文将深入探讨Go语言在构建微服务时的性能优势,包括其在内存管理、网络编程、并发模型以及工具链支持方面的特点。通过对比其他流行语言,我们将揭示Go语言如何成为微服务架构中的一股清流。
|
4天前
|
Go 索引
go语言中的循环语句
【11月更文挑战第4天】
13 2
|
4天前
|
Go C++
go语言中的条件语句
【11月更文挑战第4天】
15 2
|
9天前
|
Ubuntu 编译器 Linux
go语言中SQLite3驱动安装
【11月更文挑战第2天】
31 7
|
9天前
|
关系型数据库 Go 网络安全
go语言中PostgreSQL驱动安装
【11月更文挑战第2天】
38 5
|
9天前
|
安全 Go
用 Zap 轻松搞定 Go 语言中的结构化日志
在现代应用程序开发中,日志记录至关重要。Go 语言中有许多日志库,而 Zap 因其高性能和灵活性脱颖而出。本文详细介绍如何在 Go 项目中使用 Zap 进行结构化日志记录,并展示如何定制日志输出,满足生产环境需求。通过基础示例、SugaredLogger 的便捷使用以及自定义日志配置,帮助你在实际开发中高效管理日志。
25 1
|
8天前
|
程序员 Go
go语言中的控制结构
【11月更文挑战第3天】
84 58
|
7天前
|
监控 Go API
Go语言在微服务架构中的应用实践
在微服务架构的浪潮中,Go语言以其简洁、高效和并发处理能力脱颖而出,成为构建微服务的理想选择。本文将探讨Go语言在微服务架构中的应用实践,包括Go语言的特性如何适应微服务架构的需求,以及在实际开发中如何利用Go语言的特性来提高服务的性能和可维护性。我们将通过一个具体的案例分析,展示Go语言在微服务开发中的优势,并讨论在实际应用中可能遇到的挑战和解决方案。
|
8天前
|
存储 编译器 Go
go语言中的变量、常量、数据类型
【11月更文挑战第3天】
25 9