1811 字
9 分钟
260714GO常用数据结构

GO 常用数据结构#

一、数组与切片#

1. 数组 Array#

  • 特点:长度固定、声明时必须指定大小,属于值类型,不能动态扩容
package main
import "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 main
import (
"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 main
import (
"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 main
import (
"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 种引用类型#

  1. slice 切片

    s := make([]int, 3, 10) // 长度3,容量10
    • 分配底层数组内存,减少反复扩容开销
    • var s []int → nil 切片,无底层数组
  2. map 字典

    m := make(map[string]int, 10)
    • 初始化哈希表,避免 nil map 直接赋值 panic
  3. 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常用数据结构/
作者
anh
发布于
2026-07-14
许可协议
CC BY-NC-SA 4.0