1. 数组(Array)
数组是固定长度的相同类型元素的集合。
基本用法
go
package main
import "fmt"
func main() {
// 声明数组
var arr1 [3]int // 声明长度为3的int数组
arr2 := [3]int{1, 2, 3} // 声明并初始化
arr3 := [...]int{1, 2, 3, 4} // 编译器推导长度
fmt.Println("arr1:", arr1) // [0 0 0]
fmt.Println("arr2:", arr2) // [1 2 3]
fmt.Println("arr3:", arr3) // [1 2 3 4]
// 访问和修改元素
arr2[0] = 100
fmt.Println("arr2[0]:", arr2[0]) // 100
// 遍历数组
for i := 0; i < len(arr2); i++ {
fmt.Printf("arr2[%d] = %d\n", i, arr2[i])
}
// 使用range遍历
for index, value := range arr3 {
fmt.Printf("Index: %d, Value: %d\n", index, value)
}
}
多维数组
go
func multiDimensionalArray() {
// 二维数组
var matrix [3][3]int
// 初始化
matrix = [3][3]int{
{1, 2, 3},
{4, 5, 6},
{7, 8, 9},
}
// 访问元素
fmt.Println("matrix[1][2]:", matrix[1][2]) // 6
// 遍历二维数组
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
fmt.Printf("%d ", matrix[i][j])
}
fmt.Println()
}
}
2. 切片(Slice)
切片是动态长度的数组,是Go中最常用的集合类型。
基本用法
go
func sliceBasics() {
// 创建切片的多种方式
var s1 []int // nil切片
s2 := []int{1, 2, 3} // 字面量创建
s3 := make([]int, 3) // 长度为3,容量为3
s4 := make([]int, 3, 5) // 长度为3,容量为5
// 从数组创建切片
arr := [5]int{1, 2, 3, 4, 5}
s5 := arr[1:4] // [2, 3, 4]
s6 := arr[:3] // [1, 2, 3]
s7 := arr[2:] // [3, 4, 5]
// 切片操作
fmt.Println("长度:", len(s4)) // 3
fmt.Println("容量:", cap(s4)) // 5
// 添加元素
s2 = append(s2, 4, 5) // [1, 2, 3, 4, 5]
s2 = append(s2, []int{6, 7}...) // 追加切片
// 复制切片
s8 := make([]int, len(s2))
copy(s8, s2)
// 删除元素
s9 := []int{1, 2, 3, 4, 5}
// 删除索引2的元素
s9 = append(s9[:2], s9[3:]...)
fmt.Println("删除后:", s9) // [1, 2, 4, 5]
}
切片高级操作
go
func advancedSliceOperations() {
// 切片作为栈
stack := []int{}
// 入栈
stack = append(stack, 1) // push 1
stack = append(stack, 2) // push 2
// 出栈
if len(stack) > 0 {
top := stack[len(stack)-1]
stack = stack[:len(stack)-1]
fmt.Println("出栈:", top)
}
// 切片作为队列(低效,推荐使用container/list)
queue := []int{1, 2, 3}
// 出队
if len(queue) > 0 {
front := queue[0]
queue = queue[1:]
fmt.Println("出队:", front)
}
// 切片分割
data := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
chunkSize := 3
var chunks [][]int
for i := 0; i < len(data); i += chunkSize {
end := i + chunkSize
if end > len(data) {
end = len(data)
}
chunks = append(chunks, data[i:end])
}
fmt.Println("分块:", chunks)
}
3. 映射(Map)
Map是键值对的集合,也称为字典。
基本用法
go
func mapBasics() {
// 创建map
var m1 map[string]int // nil map
m2 := make(map[string]int) // 空map
m3 := map[string]int{ // 字面量创建
"apple": 5,
"banana": 10,
"orange": 8,
}
// 添加/修改元素
m2["apple"] = 3
m2["banana"] = 6
m2["apple"] = 4 // 修改值
// 访问元素
value := m3["apple"]
fmt.Println("apple:", value)
// 检查key是否存在
if v, ok := m3["grape"]; ok {
fmt.Println("grape exists:", v)
} else {
fmt.Println("grape not found")
}
// 删除元素
delete(m3, "orange")
// 遍历map
for key, value := range m3 {
fmt.Printf("%s: %d\n", key, value)
}
// 只遍历key
for key := range m3 {
fmt.Println("Key:", key)
}
// 只遍历value
for _, value := range m3 {
fmt.Println("Value:", value)
}
}
Map高级操作
go
func advancedMapOperations() {
// 嵌套map
students := map[string]map[string]float64{
"张三": {
"数学": 85.5,
"英语": 90.0,
},
"李四": {
"数学": 92.0,
"物理": 88.5,
},
}
// 访问嵌套map
if scores, ok := students["张三"]; ok {
fmt.Println("张三的数学成绩:", scores["数学"])
}
// 反转map(值->键)
original := map[string]int{"a": 1, "b": 2, "c": 1}
reversed := make(map[int][]string)
for key, value := range original {
reversed[value] = append(reversed[value], key)
}
fmt.Println("反转后的map:", reversed)
// map合并
map1 := map[string]int{"a": 1, "b": 2}
map2 := map[string]int{"b": 3, "c": 4}
for key, value := range map2 {
map1[key] = value // map2会覆盖map1的相同key
}
fmt.Println("合并后:", map1)
}
4. 列表(List)
使用标准库的container/list实现双向链表。
go
package main
import (
"container/list"
"fmt"
)
func listBasics() {
// 创建列表
l := list.New()
// 添加元素
l.PushBack(1) // 在尾部添加
l.PushBack(2)
l.PushFront(0) // 在头部添加
// 插入元素
for e := l.Front(); e != nil; e = e.Next() {
if e.Value == 1 {
l.InsertBefore(0.5, e) // 在1前面插入0.5
break
}
}
// 遍历列表
fmt.Print("正向遍历: ")
for e := l.Front(); e != nil; e = e.Next() {
fmt.Printf("%v ", e.Value)
}
fmt.Print("\n反向遍历: ")
for e := l.Back(); e != nil; e = e.Prev() {
fmt.Printf("%v ", e.Value)
}
// 删除元素
for e := l.Front(); e != nil; e = e.Next() {
if e.Value == 1 {
l.Remove(e)
break
}
}
fmt.Print("\n删除后: ")
for e := l.Front(); e != nil; e = e.Next() {
fmt.Printf("%v ", e.Value)
}
}
5. 集合操作示例
go
func collectionExamples() {
// 示例1:数组和切片转换
arr := [5]int{1, 2, 3, 4, 5}
slice := arr[:] // 数组转切片
// 切片转数组(Go 1.17+)
slice2 := []int{1, 2, 3, 4, 5}
var arr2 [5]int
copy(arr2[:], slice2) // 方法1
// 或
arr3 := [5]int(slice2) // 方法2(需要长度匹配)
// 示例2:切片去重
numbers := []int{1, 2, 2, 3, 4, 4, 4, 5}
unique := make([]int, 0)
seen := make(map[int]bool)
for _, num := range numbers {
if !seen[num] {
seen[num] = true
unique = append(unique, num)
}
}
fmt.Println("去重后:", unique)
// 示例3:统计词频
words := []string{"apple", "banana", "apple", "orange", "banana", "apple"}
wordCount := make(map[string]int)
for _, word := range words {
wordCount[word]++
}
fmt.Println("词频统计:", wordCount)
// 示例4:矩阵转置
matrix := [][]int{
{1, 2, 3},
{4, 5, 6},
{7, 8, 9},
}
rows := len(matrix)
cols := len(matrix[0])
transposed := make([][]int, cols)
for i := range transposed {
transposed[i] = make([]int, rows)
for j := range transposed[i] {
transposed[i][j] = matrix[j][i]
}
}
fmt.Println("转置矩阵:", transposed)
}
6. 内置函数
go
func builtinFunctions() {
// len() - 获取长度
arr := [3]int{1, 2, 3}
slice := []int{1, 2, 3, 4}
m := map[string]int{"a": 1, "b": 2}
fmt.Println("数组长度:", len(arr)) // 3
fmt.Println("切片长度:", len(slice)) // 4
fmt.Println("map长度:", len(m)) // 2
// cap() - 获取容量(仅数组、切片、通道)
fmt.Println("切片容量:", cap(slice)) // 4
// make() - 创建切片、映射、通道
s := make([]int, 3, 5)
mm := make(map[string]int)
// append() - 向切片追加元素
s = append(s, 6, 7)
// copy() - 复制切片
s2 := make([]int, len(s))
copy(s2, s)
// delete() - 删除map元素
delete(mm, "key")
// close() - 关闭通道(通道也是一种集合)
}
总结
Go语言的集合类型非常丰富,每种都有其特定的用途:
- 数组:性能最好,但长度固定
- 切片:最常用,灵活的动态数组
- 映射:键值对存储,快速查找
- 列表:双向链表,灵活插入删除
选择哪种集合取决于具体的需求:
- 大多数情况下使用切片
- 需要键值对查找时用映射
- 需要固定长度和高性能时用数组
- 需要频繁在中间插入删除时用列表
在实际开发中,切片和映射是最常用的两种集合类型。