672 lines
18 KiB
Go
672 lines
18 KiB
Go
/*
|
||
02-slices.go - Go 语言切片详解
|
||
|
||
学习目标:
|
||
1. 理解切片的概念和内部结构
|
||
2. 掌握切片的创建和初始化
|
||
3. 学会切片的各种操作
|
||
4. 了解切片的扩容机制
|
||
5. 掌握切片的实际应用场景
|
||
|
||
知识点:
|
||
- 切片的定义和特性
|
||
- 切片的创建方式
|
||
- 切片的操作(append、copy、切片表达式)
|
||
- 切片的内部结构(指针、长度、容量)
|
||
- 切片的扩容机制
|
||
- 切片的内存管理
|
||
*/
|
||
|
||
package main
|
||
|
||
import "fmt"
|
||
|
||
func main() {
|
||
fmt.Println("=== Go 语言切片详解 ===\n")
|
||
|
||
// 演示切片的基本概念
|
||
demonstrateBasicSlices()
|
||
|
||
// 演示切片的创建方式
|
||
demonstrateSliceCreation()
|
||
|
||
// 演示切片的基本操作
|
||
demonstrateSliceOperations()
|
||
|
||
// 演示切片的内部结构
|
||
demonstrateSliceInternals()
|
||
|
||
// 演示切片的扩容机制
|
||
demonstrateSliceGrowth()
|
||
|
||
// 演示切片的高级操作
|
||
demonstrateAdvancedSliceOperations()
|
||
|
||
// 演示切片的实际应用
|
||
demonstratePracticalApplications()
|
||
}
|
||
|
||
// demonstrateBasicSlices 演示切片的基本概念
|
||
func demonstrateBasicSlices() {
|
||
fmt.Println("1. 切片的基本概念:")
|
||
|
||
// 切片的基本特性
|
||
fmt.Printf(" 切片的基本特性:\n")
|
||
fmt.Printf(" - 动态数组,长度可变\n")
|
||
fmt.Printf(" - 引用类型,指向底层数组\n")
|
||
fmt.Printf(" - 有长度(len)和容量(cap)两个属性\n")
|
||
fmt.Printf(" - 零值是 nil\n")
|
||
fmt.Printf(" - 不能直接比较,只能与 nil 比较\n")
|
||
|
||
// 基本切片示例
|
||
fmt.Printf(" 基本切片示例:\n")
|
||
|
||
var slice []int // 声明切片,零值为 nil
|
||
fmt.Printf(" 声明切片: %v\n", slice)
|
||
fmt.Printf(" 切片长度: %d\n", len(slice))
|
||
fmt.Printf(" 切片容量: %d\n", cap(slice))
|
||
fmt.Printf(" 是否为 nil: %t\n", slice == nil)
|
||
|
||
// 初始化切片
|
||
numbers := []int{1, 2, 3, 4, 5}
|
||
fmt.Printf(" 初始化切片: %v\n", numbers)
|
||
fmt.Printf(" 长度: %d, 容量: %d\n", len(numbers), cap(numbers))
|
||
|
||
// 切片元素访问
|
||
fmt.Printf(" 第一个元素: %d\n", numbers[0])
|
||
fmt.Printf(" 最后一个元素: %d\n", numbers[len(numbers)-1])
|
||
|
||
fmt.Println()
|
||
}
|
||
|
||
// demonstrateSliceCreation 演示切片的创建方式
|
||
func demonstrateSliceCreation() {
|
||
fmt.Println("2. 切片的创建方式:")
|
||
|
||
// 方式1: 字面量创建
|
||
fmt.Printf(" 方式1 - 字面量创建:\n")
|
||
slice1 := []string{"Go", "Python", "Java"}
|
||
fmt.Printf(" slice1: %v (len=%d, cap=%d)\n", slice1, len(slice1), cap(slice1))
|
||
|
||
// 方式2: make 函数创建
|
||
fmt.Printf(" 方式2 - make 函数创建:\n")
|
||
slice2 := make([]int, 5) // 长度为5,容量为5
|
||
slice3 := make([]int, 3, 10) // 长度为3,容量为10
|
||
|
||
fmt.Printf(" slice2: %v (len=%d, cap=%d)\n", slice2, len(slice2), cap(slice2))
|
||
fmt.Printf(" slice3: %v (len=%d, cap=%d)\n", slice3, len(slice3), cap(slice3))
|
||
|
||
// 方式3: 从数组创建
|
||
fmt.Printf(" 方式3 - 从数组创建:\n")
|
||
arr := [6]int{1, 2, 3, 4, 5, 6}
|
||
slice4 := arr[:] // 全部元素
|
||
slice5 := arr[1:4] // 索引1到3
|
||
slice6 := arr[:3] // 开头到索引2
|
||
slice7 := arr[2:] // 索引2到结尾
|
||
|
||
fmt.Printf(" 原数组: %v\n", arr)
|
||
fmt.Printf(" arr[:]: %v (len=%d, cap=%d)\n", slice4, len(slice4), cap(slice4))
|
||
fmt.Printf(" arr[1:4]: %v (len=%d, cap=%d)\n", slice5, len(slice5), cap(slice5))
|
||
fmt.Printf(" arr[:3]: %v (len=%d, cap=%d)\n", slice6, len(slice6), cap(slice6))
|
||
fmt.Printf(" arr[2:]: %v (len=%d, cap=%d)\n", slice7, len(slice7), cap(slice7))
|
||
|
||
// 方式4: 从切片创建
|
||
fmt.Printf(" 方式4 - 从切片创建:\n")
|
||
original := []int{10, 20, 30, 40, 50}
|
||
slice8 := original[1:4]
|
||
slice9 := slice8[1:3]
|
||
|
||
fmt.Printf(" 原切片: %v\n", original)
|
||
fmt.Printf(" original[1:4]: %v (len=%d, cap=%d)\n", slice8, len(slice8), cap(slice8))
|
||
fmt.Printf(" slice8[1:3]: %v (len=%d, cap=%d)\n", slice9, len(slice9), cap(slice9))
|
||
|
||
// 方式5: 三索引切片表达式
|
||
fmt.Printf(" 方式5 - 三索引切片表达式:\n")
|
||
data := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||
slice10 := data[2:5:7] // [low:high:max] 限制容量
|
||
|
||
fmt.Printf(" data[2:5:7]: %v (len=%d, cap=%d)\n", slice10, len(slice10), cap(slice10))
|
||
|
||
fmt.Println()
|
||
}// de
|
||
monstrateSliceOperations 演示切片的基本操作
|
||
func demonstrateSliceOperations() {
|
||
fmt.Println("3. 切片的基本操作:")
|
||
|
||
// append 操作
|
||
fmt.Printf(" append 操作:\n")
|
||
var fruits []string
|
||
fmt.Printf(" 初始切片: %v (len=%d, cap=%d)\n", fruits, len(fruits), cap(fruits))
|
||
|
||
// 添加单个元素
|
||
fruits = append(fruits, "apple")
|
||
fmt.Printf(" 添加 apple: %v (len=%d, cap=%d)\n", fruits, len(fruits), cap(fruits))
|
||
|
||
// 添加多个元素
|
||
fruits = append(fruits, "banana", "cherry")
|
||
fmt.Printf(" 添加多个: %v (len=%d, cap=%d)\n", fruits, len(fruits), cap(fruits))
|
||
|
||
// 添加另一个切片
|
||
moreFruits := []string{"date", "elderberry"}
|
||
fruits = append(fruits, moreFruits...)
|
||
fmt.Printf(" 添加切片: %v (len=%d, cap=%d)\n", fruits, len(fruits), cap(fruits))
|
||
|
||
// copy 操作
|
||
fmt.Printf(" copy 操作:\n")
|
||
source := []int{1, 2, 3, 4, 5}
|
||
dest := make([]int, 3)
|
||
|
||
n := copy(dest, source)
|
||
fmt.Printf(" 源切片: %v\n", source)
|
||
fmt.Printf(" 目标切片: %v\n", dest)
|
||
fmt.Printf(" 复制了 %d 个元素\n", n)
|
||
|
||
// copy 的特殊用法
|
||
numbers := []int{1, 2, 3, 4, 5, 6}
|
||
copy(numbers[2:], numbers[1:]) // 向右移动元素
|
||
fmt.Printf(" 移动后: %v\n", numbers)
|
||
|
||
// 切片遍历
|
||
fmt.Printf(" 切片遍历:\n")
|
||
colors := []string{"red", "green", "blue", "yellow"}
|
||
|
||
// 使用索引遍历
|
||
fmt.Printf(" 使用索引遍历:\n")
|
||
for i := 0; i < len(colors); i++ {
|
||
fmt.Printf(" colors[%d] = %s\n", i, colors[i])
|
||
}
|
||
|
||
// 使用 range 遍历
|
||
fmt.Printf(" 使用 range 遍历:\n")
|
||
for index, color := range colors {
|
||
fmt.Printf(" colors[%d] = %s\n", index, color)
|
||
}
|
||
|
||
// 切片修改
|
||
fmt.Printf(" 切片修改:\n")
|
||
nums := []int{10, 20, 30, 40, 50}
|
||
fmt.Printf(" 原切片: %v\n", nums)
|
||
|
||
nums[2] = 99
|
||
fmt.Printf(" 修改索引2: %v\n", nums)
|
||
|
||
// 批量修改
|
||
for i := range nums {
|
||
nums[i] *= 2
|
||
}
|
||
fmt.Printf(" 全部乘以2: %v\n", nums)
|
||
|
||
fmt.Println()
|
||
}
|
||
|
||
// demonstrateSliceInternals 演示切片的内部结构
|
||
func demonstrateSliceInternals() {
|
||
fmt.Println("4. 切片的内部结构:")
|
||
|
||
fmt.Printf(" 切片内部结构包含三个字段:\n")
|
||
fmt.Printf(" - 指针:指向底层数组的指针\n")
|
||
fmt.Printf(" - 长度:切片中元素的个数\n")
|
||
fmt.Printf(" - 容量:从切片起始位置到底层数组末尾的元素个数\n")
|
||
|
||
// 演示切片和底层数组的关系
|
||
fmt.Printf(" 切片和底层数组的关系:\n")
|
||
arr := [8]int{0, 1, 2, 3, 4, 5, 6, 7}
|
||
slice := arr[2:6]
|
||
|
||
fmt.Printf(" 原数组: %v\n", arr)
|
||
fmt.Printf(" 切片 arr[2:6]: %v (len=%d, cap=%d)\n", slice, len(slice), cap(slice))
|
||
|
||
// 修改切片影响底层数组
|
||
slice[1] = 99
|
||
fmt.Printf(" 修改切片后:\n")
|
||
fmt.Printf(" 数组: %v\n", arr)
|
||
fmt.Printf(" 切片: %v\n", slice)
|
||
|
||
// 多个切片共享底层数组
|
||
fmt.Printf(" 多个切片共享底层数组:\n")
|
||
slice1 := arr[1:4]
|
||
slice2 := arr[3:7]
|
||
|
||
fmt.Printf(" slice1 (arr[1:4]): %v\n", slice1)
|
||
fmt.Printf(" slice2 (arr[3:7]): %v\n", slice2)
|
||
|
||
slice1[2] = 888 // 修改 arr[3]
|
||
fmt.Printf(" 修改 slice1[2] 后:\n")
|
||
fmt.Printf(" slice1: %v\n", slice1)
|
||
fmt.Printf(" slice2: %v (第一个元素也变了)\n", slice2)
|
||
fmt.Printf(" 数组: %v\n", arr)
|
||
|
||
// 切片的容量计算
|
||
fmt.Printf(" 切片的容量计算:\n")
|
||
data := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||
|
||
s1 := data[2:5] // 从索引2开始,容量是到数组末尾
|
||
s2 := data[2:5:7] // 限制容量到索引7
|
||
|
||
fmt.Printf(" data[2:5]: %v (len=%d, cap=%d)\n", s1, len(s1), cap(s1))
|
||
fmt.Printf(" data[2:5:7]: %v (len=%d, cap=%d)\n", s2, len(s2), cap(s2))
|
||
|
||
fmt.Println()
|
||
}
|
||
|
||
// demonstrateSliceGrowth 演示切片的扩容机制
|
||
func demonstrateSliceGrowth() {
|
||
fmt.Println("5. 切片的扩容机制:")
|
||
|
||
fmt.Printf(" 切片扩容规则:\n")
|
||
fmt.Printf(" - 当 append 时容量不足,会创建新的底层数组\n")
|
||
fmt.Printf(" - 新容量通常是原容量的2倍(小切片)\n")
|
||
fmt.Printf(" - 大切片的扩容策略更复杂\n")
|
||
|
||
// 演示扩容过程
|
||
fmt.Printf(" 扩容过程演示:\n")
|
||
var slice []int
|
||
|
||
for i := 0; i < 10; i++ {
|
||
oldCap := cap(slice)
|
||
slice = append(slice, i)
|
||
newCap := cap(slice)
|
||
|
||
if newCap != oldCap {
|
||
fmt.Printf(" 扩容: 长度 %d, 容量 %d -> %d\n", len(slice), oldCap, newCap)
|
||
}
|
||
}
|
||
|
||
fmt.Printf(" 最终切片: %v (len=%d, cap=%d)\n", slice, len(slice), cap(slice))
|
||
|
||
// 预分配容量避免频繁扩容
|
||
fmt.Printf(" 预分配容量优化:\n")
|
||
|
||
// 不好的做法:频繁扩容
|
||
var badSlice []int
|
||
for i := 0; i < 1000; i++ {
|
||
badSlice = append(badSlice, i)
|
||
}
|
||
fmt.Printf(" 频繁扩容结果: len=%d, cap=%d\n", len(badSlice), cap(badSlice))
|
||
|
||
// 好的做法:预分配容量
|
||
goodSlice := make([]int, 0, 1000) // 预分配容量1000
|
||
for i := 0; i < 1000; i++ {
|
||
goodSlice = append(goodSlice, i)
|
||
}
|
||
fmt.Printf(" 预分配结果: len=%d, cap=%d\n", len(goodSlice), cap(goodSlice))
|
||
|
||
// 扩容时的内存重新分配
|
||
fmt.Printf(" 扩容时的内存重新分配:\n")
|
||
original := []int{1, 2, 3}
|
||
fmt.Printf(" 原切片: %v (cap=%d)\n", original, cap(original))
|
||
|
||
// 创建基于原切片的新切片
|
||
derived := original[1:2]
|
||
fmt.Printf(" 派生切片: %v (cap=%d)\n", derived, cap(derived))
|
||
|
||
// 扩容派生切片
|
||
derived = append(derived, 99, 100, 101, 102)
|
||
fmt.Printf(" 扩容后派生切片: %v (cap=%d)\n", derived, cap(derived))
|
||
fmt.Printf(" 原切片: %v (未受影响)\n", original)
|
||
|
||
fmt.Println()
|
||
}
|
||
|
||
// demonstrateAdvancedSliceOperations 演示切片的高级操作
|
||
func demonstrateAdvancedSliceOperations() {
|
||
fmt.Println("6. 切片的高级操作:")
|
||
|
||
// 切片删除元素
|
||
fmt.Printf(" 切片删除元素:\n")
|
||
numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
||
fmt.Printf(" 原切片: %v\n", numbers)
|
||
|
||
// 删除索引为3的元素
|
||
index := 3
|
||
numbers = append(numbers[:index], numbers[index+1:]...)
|
||
fmt.Printf(" 删除索引3: %v\n", numbers)
|
||
|
||
// 删除多个元素(索引1到3)
|
||
start, end := 1, 4
|
||
numbers = append(numbers[:start], numbers[end:]...)
|
||
fmt.Printf(" 删除索引1-3: %v\n", numbers)
|
||
|
||
// 切片插入元素
|
||
fmt.Printf(" 切片插入元素:\n")
|
||
data := []int{1, 2, 5, 6}
|
||
fmt.Printf(" 原切片: %v\n", data)
|
||
|
||
// 在索引2插入元素3和4
|
||
insertIndex := 2
|
||
insertValues := []int{3, 4}
|
||
|
||
// 方法1:使用 append 和切片操作
|
||
data = append(data[:insertIndex], append(insertValues, data[insertIndex:]...)...)
|
||
fmt.Printf(" 插入3,4: %v\n", data)
|
||
|
||
// 切片反转
|
||
fmt.Printf(" 切片反转:\n")
|
||
original := []string{"a", "b", "c", "d", "e"}
|
||
reversed := make([]string, len(original))
|
||
|
||
for i, v := range original {
|
||
reversed[len(original)-1-i] = v
|
||
}
|
||
|
||
fmt.Printf(" 原切片: %v\n", original)
|
||
fmt.Printf(" 反转后: %v\n", reversed)
|
||
|
||
// 就地反转
|
||
toReverse := []int{1, 2, 3, 4, 5}
|
||
fmt.Printf(" 就地反转前: %v\n", toReverse)
|
||
|
||
for i, j := 0, len(toReverse)-1; i < j; i, j = i+1, j-1 {
|
||
toReverse[i], toReverse[j] = toReverse[j], toReverse[i]
|
||
}
|
||
fmt.Printf(" 就地反转后: %v\n", toReverse)
|
||
|
||
// 切片去重
|
||
fmt.Printf(" 切片去重:\n")
|
||
withDuplicates := []int{1, 2, 2, 3, 3, 3, 4, 5, 5}
|
||
unique := removeDuplicates(withDuplicates)
|
||
|
||
fmt.Printf(" 有重复: %v\n", withDuplicates)
|
||
fmt.Printf(" 去重后: %v\n", unique)
|
||
|
||
// 切片过滤
|
||
fmt.Printf(" 切片过滤:\n")
|
||
allNumbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
||
evenNumbers := filter(allNumbers, func(n int) bool { return n%2 == 0 })
|
||
|
||
fmt.Printf(" 所有数字: %v\n", allNumbers)
|
||
fmt.Printf(" 偶数: %v\n", evenNumbers)
|
||
|
||
fmt.Println()
|
||
}/
|
||
/ demonstratePracticalApplications 演示切片的实际应用
|
||
func demonstratePracticalApplications() {
|
||
fmt.Println("7. 切片的实际应用:")
|
||
|
||
// 应用1: 动态数组
|
||
fmt.Printf(" 应用1 - 动态数组(购物车):\n")
|
||
var cart []string
|
||
|
||
// 添加商品
|
||
cart = append(cart, "笔记本电脑", "鼠标", "键盘")
|
||
fmt.Printf(" 购物车: %v\n", cart)
|
||
|
||
// 移除商品(移除鼠标)
|
||
for i, item := range cart {
|
||
if item == "鼠标" {
|
||
cart = append(cart[:i], cart[i+1:]...)
|
||
break
|
||
}
|
||
}
|
||
fmt.Printf(" 移除鼠标后: %v\n", cart)
|
||
|
||
// 应用2: 缓冲区和队列
|
||
fmt.Printf(" 应用2 - 简单队列实现:\n")
|
||
queue := NewQueue()
|
||
|
||
// 入队
|
||
queue.Enqueue("任务1")
|
||
queue.Enqueue("任务2")
|
||
queue.Enqueue("任务3")
|
||
fmt.Printf(" 队列状态: %v\n", queue.items)
|
||
|
||
// 出队
|
||
item := queue.Dequeue()
|
||
fmt.Printf(" 出队: %s\n", item)
|
||
fmt.Printf(" 队列状态: %v\n", queue.items)
|
||
|
||
// 应用3: 栈实现
|
||
fmt.Printf(" 应用3 - 简单栈实现:\n")
|
||
stack := NewStack()
|
||
|
||
// 入栈
|
||
stack.Push(10)
|
||
stack.Push(20)
|
||
stack.Push(30)
|
||
fmt.Printf(" 栈状态: %v\n", stack.items)
|
||
|
||
// 出栈
|
||
value := stack.Pop()
|
||
fmt.Printf(" 出栈: %d\n", value)
|
||
fmt.Printf(" 栈状态: %v\n", stack.items)
|
||
|
||
// 应用4: 数据处理管道
|
||
fmt.Printf(" 应用4 - 数据处理管道:\n")
|
||
rawData := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
||
|
||
// 管道处理:过滤偶数 -> 平方 -> 求和
|
||
evenNumbers := filter(rawData, func(n int) bool { return n%2 == 0 })
|
||
squares := mapInts(evenNumbers, func(n int) int { return n * n })
|
||
sum := reduce(squares, 0, func(acc, n int) int { return acc + n })
|
||
|
||
fmt.Printf(" 原始数据: %v\n", rawData)
|
||
fmt.Printf(" 偶数: %v\n", evenNumbers)
|
||
fmt.Printf(" 平方: %v\n", squares)
|
||
fmt.Printf(" 求和: %d\n", sum)
|
||
|
||
// 应用5: 分页处理
|
||
fmt.Printf(" 应用5 - 分页处理:\n")
|
||
allItems := []string{
|
||
"item1", "item2", "item3", "item4", "item5",
|
||
"item6", "item7", "item8", "item9", "item10",
|
||
"item11", "item12", "item13", "item14", "item15",
|
||
}
|
||
|
||
pageSize := 5
|
||
totalPages := (len(allItems) + pageSize - 1) / pageSize
|
||
|
||
for page := 0; page < totalPages; page++ {
|
||
start := page * pageSize
|
||
end := start + pageSize
|
||
if end > len(allItems) {
|
||
end = len(allItems)
|
||
}
|
||
|
||
pageItems := allItems[start:end]
|
||
fmt.Printf(" 第%d页: %v\n", page+1, pageItems)
|
||
}
|
||
|
||
// 应用6: 批处理
|
||
fmt.Printf(" 应用6 - 批处理:\n")
|
||
tasks := []string{
|
||
"task1", "task2", "task3", "task4", "task5",
|
||
"task6", "task7", "task8", "task9", "task10",
|
||
}
|
||
|
||
batchSize := 3
|
||
for i := 0; i < len(tasks); i += batchSize {
|
||
end := i + batchSize
|
||
if end > len(tasks) {
|
||
end = len(tasks)
|
||
}
|
||
|
||
batch := tasks[i:end]
|
||
fmt.Printf(" 处理批次: %v\n", batch)
|
||
// 这里可以并发处理这个批次
|
||
}
|
||
|
||
// 应用7: 滑动窗口
|
||
fmt.Printf(" 应用7 - 滑动窗口(移动平均):\n")
|
||
prices := []float64{10.5, 11.2, 10.8, 12.1, 11.9, 12.5, 11.8, 12.3, 13.0, 12.7}
|
||
windowSize := 3
|
||
|
||
fmt.Printf(" 价格序列: %v\n", prices)
|
||
fmt.Printf(" %d日移动平均:\n", windowSize)
|
||
|
||
for i := 0; i <= len(prices)-windowSize; i++ {
|
||
window := prices[i : i+windowSize]
|
||
average := 0.0
|
||
for _, price := range window {
|
||
average += price
|
||
}
|
||
average /= float64(len(window))
|
||
fmt.Printf(" 第%d-%d日: %.2f\n", i+1, i+windowSize, average)
|
||
}
|
||
|
||
fmt.Println()
|
||
}
|
||
|
||
// ========== 辅助函数和类型 ==========
|
||
|
||
// 去重函数
|
||
func removeDuplicates(slice []int) []int {
|
||
seen := make(map[int]bool)
|
||
var result []int
|
||
|
||
for _, v := range slice {
|
||
if !seen[v] {
|
||
seen[v] = true
|
||
result = append(result, v)
|
||
}
|
||
}
|
||
|
||
return result
|
||
}
|
||
|
||
// 过滤函数
|
||
func filter(slice []int, predicate func(int) bool) []int {
|
||
var result []int
|
||
for _, v := range slice {
|
||
if predicate(v) {
|
||
result = append(result, v)
|
||
}
|
||
}
|
||
return result
|
||
}
|
||
|
||
// 映射函数
|
||
func mapInts(slice []int, mapper func(int) int) []int {
|
||
result := make([]int, len(slice))
|
||
for i, v := range slice {
|
||
result[i] = mapper(v)
|
||
}
|
||
return result
|
||
}
|
||
|
||
// 归约函数
|
||
func reduce(slice []int, initial int, reducer func(int, int) int) int {
|
||
result := initial
|
||
for _, v := range slice {
|
||
result = reducer(result, v)
|
||
}
|
||
return result
|
||
}
|
||
|
||
// 简单队列实现
|
||
type Queue struct {
|
||
items []string
|
||
}
|
||
|
||
func NewQueue() *Queue {
|
||
return &Queue{items: make([]string, 0)}
|
||
}
|
||
|
||
func (q *Queue) Enqueue(item string) {
|
||
q.items = append(q.items, item)
|
||
}
|
||
|
||
func (q *Queue) Dequeue() string {
|
||
if len(q.items) == 0 {
|
||
return ""
|
||
}
|
||
|
||
item := q.items[0]
|
||
q.items = q.items[1:]
|
||
return item
|
||
}
|
||
|
||
func (q *Queue) IsEmpty() bool {
|
||
return len(q.items) == 0
|
||
}
|
||
|
||
// 简单栈实现
|
||
type Stack struct {
|
||
items []int
|
||
}
|
||
|
||
func NewStack() *Stack {
|
||
return &Stack{items: make([]int, 0)}
|
||
}
|
||
|
||
func (s *Stack) Push(item int) {
|
||
s.items = append(s.items, item)
|
||
}
|
||
|
||
func (s *Stack) Pop() int {
|
||
if len(s.items) == 0 {
|
||
return 0
|
||
}
|
||
|
||
index := len(s.items) - 1
|
||
item := s.items[index]
|
||
s.items = s.items[:index]
|
||
return item
|
||
}
|
||
|
||
func (s *Stack) IsEmpty() bool {
|
||
return len(s.items) == 0
|
||
}
|
||
|
||
func (s *Stack) Peek() int {
|
||
if len(s.items) == 0 {
|
||
return 0
|
||
}
|
||
return s.items[len(s.items)-1]
|
||
}
|
||
|
||
/*
|
||
运行这个程序:
|
||
go run 02-slices.go
|
||
|
||
学习要点:
|
||
1. 切片是动态数组,长度可变,是引用类型
|
||
2. 切片有长度(len)和容量(cap)两个属性
|
||
3. 切片的零值是 nil,不能直接比较
|
||
4. append 操作可能触发扩容,创建新的底层数组
|
||
5. 多个切片可以共享同一个底层数组
|
||
|
||
切片的内部结构:
|
||
1. 指针:指向底层数组的指针
|
||
2. 长度:切片中元素的个数
|
||
3. 容量:从切片起始位置到底层数组末尾的元素个数
|
||
|
||
创建切片的方式:
|
||
1. 字面量:[]Type{values...}
|
||
2. make函数:make([]Type, len, cap)
|
||
3. 从数组/切片:array[start:end]
|
||
4. 三索引表达式:slice[start:end:cap]
|
||
|
||
切片操作:
|
||
1. append:添加元素,可能扩容
|
||
2. copy:复制元素
|
||
3. 切片表达式:获取子切片
|
||
4. 遍历:for range 或索引循环
|
||
|
||
扩容机制:
|
||
1. 容量不足时创建新数组
|
||
2. 小切片通常扩容为原来的2倍
|
||
3. 大切片扩容策略更复杂
|
||
4. 预分配容量可以避免频繁扩容
|
||
|
||
最佳实践:
|
||
1. 预分配容量避免频繁扩容
|
||
2. 使用三索引表达式限制容量
|
||
3. 注意切片共享底层数组的问题
|
||
4. 大切片传参考虑性能影响
|
||
5. 及时释放不需要的切片引用
|
||
|
||
常见应用场景:
|
||
1. 动态数组和列表
|
||
2. 栈和队列实现
|
||
3. 缓冲区和管道
|
||
4. 数据处理和过滤
|
||
5. 分页和批处理
|
||
6. 滑动窗口算法
|
||
|
||
注意事项:
|
||
1. 切片是引用类型,修改会影响底层数组
|
||
2. 切片扩容会创建新数组,断开与原数组的联系
|
||
3. 空切片和nil切片的区别
|
||
4. 切片越界会panic
|
||
5. 大切片的内存泄漏问题
|
||
*/ |