在Go语言的日常开发中,我们经常会遇到需要存储多组键值对数据的场景,这时候单独使用切片或者映射都无法满足需求,就需要创建包含映射的切片结构。这种结构可以同时发挥切片的有序存储特性和映射的键值对快速查询特性,适用于很多复杂的数据存储场景。

基础创建方式
包含映射的切片结构,本质上是一个切片,切片中的每个元素都是一个映射。我们可以直接声明并初始化这种结构,示例代码如下:
package main
import "fmt"
func main() {
// 声明并初始化包含map的切片
var sliceWithMap []map[string]int
// 初始化切片,指定初始长度为2
sliceWithMap = make([]map[string]int, 2)
// 初始化切片中的每个map元素
sliceWithMap[0] = make(map[string]int)
sliceWithMap[0]["a"] = 1
sliceWithMap[0]["b"] = 2
sliceWithMap[1] = make(map[string]int)
sliceWithMap[1]["c"] = 3
sliceWithMap[1]["d"] = 4
fmt.Println(sliceWithMap)
}
这里需要注意,切片初始化之后,内部的映射元素默认是nil,必须逐个对每个映射进行初始化之后才能赋值,否则会触发panic。
动态添加映射元素
如果我们不确定初始需要多少个映射元素,也可以使用append方法动态向切片中添加映射,这种方式更加灵活,示例代码如下:
package main
import "fmt"
func main() {
// 声明空的包含map的切片
sliceWithMap := []map[string]string{}
// 创建第一个map并添加到切片
firstMap := make(map[string]string)
firstMap["name"] = "张三"
firstMap["age"] = "20"
sliceWithMap = append(sliceWithMap, firstMap)
// 创建第二个map并添加到切片
secondMap := make(map[string]string)
secondMap["name"] = "李四"
secondMap["age"] = "22"
sliceWithMap = append(sliceWithMap, secondMap)
// 遍历输出切片中的内容
for index, m := range sliceWithMap {
fmt.Printf("第%d个映射的内容:%vn", index+1, m)
}
}
常见错误及解决方法
未初始化映射直接赋值
很多新手会写出下面的代码,直接给切片中的映射元素赋值,但是没有先初始化映射:
package main
func main() {
sliceWithMap := make([]map[string]int, 1)
// 错误写法,此时sliceWithMap[0]是nil,直接赋值会panic
sliceWithMap[0]["test"] = 1
}
正确的做法是在赋值前先初始化对应的映射:
package main
func main() {
sliceWithMap := make([]map[string]int, 1)
// 先初始化映射
sliceWithMap[0] = make(map[string]int)
sliceWithMap[0]["test"] = 1
}
映射引用导致的覆盖问题
如果我们先创建一个映射,然后多次把这个映射append到切片中,修改其中一个元素的内容,其他元素也会被修改,因为切片中的元素都是同一个映射的引用:
package main
import "fmt"
func main() {
sliceWithMap := []map[string]int{}
tempMap := make(map[string]int)
tempMap["num"] = 1
// 第一次添加
sliceWithMap = append(sliceWithMap, tempMap)
// 第二次添加同一个映射
sliceWithMap = append(sliceWithMap, tempMap)
// 修改第一个元素的内容
sliceWithMap[0]["num"] = 2
// 输出会发现两个元素的内容都变成了2
fmt.Println(sliceWithMap)
}
解决方法是每次添加的时候都创建新的映射实例:
package main
import "fmt"
func main() {
sliceWithMap := []map[string]int{}
// 第一次添加,创建新的映射
m1 := make(map[string]int)
m1["num"] = 1
sliceWithMap = append(sliceWithMap, m1)
// 第二次添加,创建新的映射
m2 := make(map[string]int)
m2["num"] = 1
sliceWithMap = append(sliceWithMap, m2)
// 修改第一个元素的内容,第二个不会受影响
sliceWithMap[0]["num"] = 2
fmt.Println(sliceWithMap)
}
适用场景说明
这种包含映射的切片结构适合需要按顺序存储多组键值对数据的场景,比如接口返回的多条记录,每条记录都是不同的字段和对应的值,这时候用这种结构就可以很方便地存储和传递数据。如果是需要快速通过键查询整个结构,也可以考虑使用映射的映射结构,开发者可以根据实际需求选择合适的复合结构。