1811 字
9 分钟
260714GO常用数据结构
GO 常用数据结构
一、数组与切片
1. 数组 Array
- 特点:长度固定、声明时必须指定大小,属于值类型,不能动态扩容
package mainimport "fmt"
func main() { // 声明长度为5的int数组,默认零值全为0 var arr [5]int fmt.Println(arr) // [0 0 0 0 0]
// 下标赋值 arr[0] = 10 arr[1] = 20 fmt.Println(arr) // [10 20 0 0 0]
// 初始化数组 nums := [5]int{1, 2, 3, 4, 5} fmt.Println(nums) // [1 2 3 4 5]
// 获取数组长度 fmt.Println(len(nums)) // 5}- 缺点:长度固定,灵活性差,实际开发很少直接使用,优先用切片 slice
2. 切片 Slice
- 特点:动态可变数组,类型写法
[]int(无固定长度数字),引用类型,底层依托数组
创建方式
func main() { // 方式1:字面量创建 nums := []int{1, 2, 3, 4, 5} fmt.Println(nums) // [1 2 3 4 5]
// 方式2:make 指定长度 a := make([]int, 5) fmt.Println(a) // [0 0 0 0 0]
// 方式3:make 指定长度 + 容量 b := make([]int, 3, 10) fmt.Println(b) // [0 0 0] fmt.Printf("长度=%d, 容量=%d\n", len(b), cap(b)) // len=3, cap=10
// 下标访问修改 nums[0] = 100 fmt.Println(nums[0]) // 100
// range遍历 for _, v := range nums { fmt.Print(v, " ") } fmt.Println()}len():当前实际元素数量cap():底层数组总容量;超出容量时 Go 自动扩容,重新分配底层数组...:切片展开语法,把切片拆成独立参数传给 append
切片增删 & 截取
func main() { nums := []int{1, 2, 3}
// 末尾追加元素 nums = append(nums, 4) nums = append(nums, 5, 6) fmt.Println(nums) // [1 2 3 4 5 6]
// 切片截取 nums[start:end]:含start,不含end fmt.Println(nums[1:4]) // [2 3 4] fmt.Println(nums[:3]) // [1 2 3] 省略start:从头开始 fmt.Println(nums[3:]) // [4 5 6] 省略end:到末尾
// 删除索引 i 的元素 i := 2 nums = append(nums[:i], nums[i+1:]...) fmt.Println(nums) // [1 2 4 5 6]
// 在索引 i 插入元素 i = 1 nums = append(nums[:i], append([]int{99}, nums[i:]...)...) fmt.Println(nums) // [1 99 2 4 5 6]}二维切片
func main() { rows, cols := 3, 4 // 创建外层切片 grid := make([][]int, rows) // 逐行创建内层一维切片 for i := 0; i < rows; i++ { grid[i] = make([]int, cols) }
// 赋值 grid[0][0] = 1 grid[1][2] = 5 grid[2][3] = 9
fmt.Printf("%d 行 %d 列\n", len(grid), len(grid[0]))}- 内存结构
grid (外层切片) ├─ grid[0] → [0, 0, 0, 0] ├─ grid[1] → [0, 0, 0, 0] └─ grid[2] → [0, 0, 0, 0]二、字符串 string
基础特性
- 字符串不可直接修改单个字符(底层是只读字节数组)
len(s):获取字节长度;下标访问得到byte字节range遍历可正确读取 Unicode 字符(rune)
func main() { s := "Hello, Go!" fmt.Println(len(s)) // 10 字节 fmt.Println(s[0]) // 72 (H的ASCII码) fmt.Printf("%c\n", s[0]) // H fmt.Println(s[0:5]) // Hello 截取
// 字符串拼接 greeting := "你好" + ",世界" fmt.Println(greeting)
// 字符串比较 fmt.Println("hello" == "hello") // true
// range遍历 Unicode for _, ch := range "Hello" { fmt.Printf("%c ", ch) } fmt.Println()
// s[0] = 'h' // ❌ 编译报错:字符串不可变}[]byte 与 []rune 转换
[]byte:按字节处理,适合纯 ASCII 英文/数字[]rune:按 Unicode 字符处理,适合中文、emoji 等多字节字符
s := "abc中文"b := []byte(s) // 转为字节数组r := []rune(s) // 转为 Unicode 字符数组newS := string(b) // 转回字符串strings 包常用函数
package mainimport ( "fmt" "strings")func main() { s := "Hello, World!" fmt.Println(strings.Contains(s, "World")) // 是否包含子串 true fmt.Println(strings.Index(s, "World")) // 子串起始索引 7 fmt.Println(strings.HasPrefix(s, "Hello")) // 是否前缀 true fmt.Println(strings.HasSuffix(s, "World!")) // 是否后缀 true
csv := "apple,banana,cherry" parts := strings.Split(csv, ",") // 分割字符串 fmt.Println(strings.Join(parts, " - ")) // 拼接切片 fmt.Println(strings.Replace("aabbaabb", "aa", "cc", -1)) // 全部替换 fmt.Println(strings.Repeat("Go", 3)) // 重复字符串 GoGoGo fmt.Println(strings.ToUpper("hello")) // 转大写 fmt.Println(strings.ToLower("HELLO")) // 转小写}strconv 包:字符串和数字互转
package mainimport ( "fmt" "strconv")func main() { // Atoi:字符串转int n, err := strconv.Atoi("123") if err == nil { fmt.Println(n) // 123 }
// Itoa:int转字符串 s := strconv.Itoa(42) fmt.Println(s) // 42
// ParseInt:指定进制转整数 num, _ := strconv.ParseInt("ff", 16, 64) fmt.Println(num) // 255
// FormatInt:整数转指定进制字符串 binStr := strconv.FormatInt(255, 2) fmt.Println(binStr) // 11111111}- 刷题高频:
strconv.Atoi/strconv.Itoa
三、结构体 struct
定义和初始化
- Go 没有 class,用 struct 聚合多个字段,可用于节点、实体建模
type Student struct { Name string Age int Score float64}
func main() { // 按字段名初始化(推荐) s1 := Student{Name: "Alice", Age: 20, Score: 95.5} fmt.Println(s1)
// 按顺序初始化(不推荐) s2 := Student{"Bob", 21, 88.0} fmt.Println(s2)
// 访问&修改字段 fmt.Println(s1.Name) s1.Age = 21 fmt.Println(s1.Age)
// 零值结构体 var s3 Student fmt.Println(s3)}结构体指针
Go 自动解引用指针字段,无需手动写 (*p).X
type Point struct { X int Y int}
func moveRight(p *Point, distance int) { p.X += distance // 直接使用 p.X}
func main() { p := &Point{X: 3, Y: 4} fmt.Println(p.X) // 3 moveRight(p, 10) fmt.Println(*p) // {13 4}}四、哈希表 map
基础用法
- 键值对存储,查找/增删效率高;必须用 make/字面量初始化,nil map 不能直接赋值
func main() { // make 创建空 map m := make(map[string]int) m["apple"] = 3 m["banana"] = 5 m["cherry"] = 2 fmt.Println(m["apple"]) // 3
m["apple"] = 10 // 修改值 delete(m, "cherry") // 删除键 fmt.Println(len(m)) // 长度 2
// 字面量初始化 scores := map[string]int{ "Alice": 90, "Bob": 85, "Carol": 92, }
// 遍历map(无序) for k, v := range scores { fmt.Printf("%s: %d\n", k, v) }}判断 key 是否存在
func main() { m := map[string]int{"apple": 3, "banana": 5} val, ok := m["apple"] fmt.Println(val, ok) // 3 true
val, ok = m["grape"] fmt.Println(val, ok) // 0 false
if val, ok := m["banana"]; ok { fmt.Printf("banana 存在,值为 %d\n", val) }}哈希集合(用 map[T]struct{} 模拟)
struct{}空结构体不占用内存,适合实现集合去重
func main() { set := make(map[int]struct{}) set[1] = struct{}{} set[2] = struct{}{} set[3] = struct{}{} set[2] = struct{}{} // 重复添加无效
fmt.Println(len(set)) // 3
// 判断存在 if _, ok := set[2]; ok { fmt.Println("2 存在") }
delete(set, 2) // 删除元素 for k := range set { fmt.Print(k, " ") } fmt.Println()}五、栈(用切片模拟)
后进先出(LIFO)
func main() { stack := []int{} // 入栈 stack = append(stack, 1) stack = append(stack, 2) stack = append(stack, 3) fmt.Println(stack) // [1 2 3]
// 栈顶元素 top := stack[len(stack)-1] fmt.Println("栈顶:", top) // 3
// 出栈 stack = stack[:len(stack)-1] fmt.Println(stack) // [1 2]
// 判空 fmt.Println(len(stack) == 0)}- 入栈:
stack = append(stack, x) - 栈顶:
stack[len(stack)-1] - 出栈:
stack = stack[:len(stack)-1] - 判空:
len(stack) == 0
六、队列(FIFO 先进先出)
container/list 双向链表实现
package mainimport ( "container/list" "fmt")func main() { queue := list.New() // 入队 queue.PushBack(10) queue.PushBack(20) queue.PushBack(30) fmt.Println("大小:", queue.Len()) // 3
// 队头元素(类型断言) front := queue.Front().Value.(int) fmt.Println("队头:", front) // 10
// 出队 queue.Remove(queue.Front()) fmt.Println("新队头:", queue.Front().Value.(int)) // 20
// 遍历 for e := queue.Front(); e != nil; e = e.Next() { fmt.Print(e.Value.(int), " ") } fmt.Println()}- 入队:
queue.PushBack(x) - 队头:
queue.Front().Value.(类型断言) - 出队:
queue.Remove(queue.Front()) - 判空:
queue.Len() == 0
七、make 初始化规则
✅ make 仅用于 3 种引用类型
-
slice 切片
s := make([]int, 3, 10) // 长度3,容量10- 分配底层数组内存,减少反复扩容开销
- var s []int → nil 切片,无底层数组
-
map 字典
m := make(map[string]int, 10)- 初始化哈希表,避免 nil map 直接赋值 panic
-
chan 通道
ch := make(chan int, 5)- 初始化缓冲区,用于 goroutine 通信
❌ 不能用 make 的类型
普通变量、定长数组 [N]int、string、bool、int、struct 等,直接声明即可
❗ new 和 make 的区别
new(T):返回 *T 指针,只分配基础结构内存,不初始化底层数据结构s := new([]int) // 仅切片结构体,无底层数组,不能直接索引赋值make(T, ...):返回原始引用类型,初始化底层数组/哈希表/缓冲区
260714GO常用数据结构
https://fuwari.vercel.app/posts/260714go常用数据结构/