03.切片操作
在 Go 语言中,切片(slice)提供了许多强大的操作,可以帮助开发者灵活地处理数据。下面是一些常见的切片操作及其示例:
1. 创建和初始化切片
使用字面量初始化
package main import "fmt" func main() { slice := []int{1, 2, 3, 4, 5} fmt.Println("Slice:", slice) }
使用 make
函数初始化
package main import "fmt" func main() { slice := make([]int, 5) fmt.Println("Slice with make:", slice) }
从数组或其他切片创建
package main import "fmt" func main() { arr := [5]int{10, 20, 30, 40, 50} slice := arr[1:4] fmt.Println("Slice from array:", slice) }
2. 访问和修改切片元素
访问元素
package main import "fmt" func main() { slice := []int{1, 2, 3, 4, 5} fmt.Println("Element at index 0:", slice[0]) }
修改元素
package main import "fmt" func main() { slice := []int{1, 2, 3, 4, 5} slice[1] = 20 fmt.Println("Modified slice:", slice) }
3. 获取切片的长度和容量
获取长度
package main import "fmt" func main() { slice := []int{1, 2, 3, 4, 5} fmt.Println("Length:", len(slice)) }
获取容量
package main import "fmt" func main() { slice := []int{1, 2, 3, 4, 5} fmt.Println("Capacity:", cap(slice)) }
4. 动态增加切片元素
使用 append
函数
package main import "fmt" func main() { slice := []int{1, 2, 3} slice = append(slice, 4, 5) fmt.Println("Appended slice:", slice) }
5. 切片的切片操作
从切片中创建子切片
package main import "fmt" func main() { slice := []int{1, 2, 3, 4, 5} subSlice := slice[1:4] fmt.Println("Sub-slice:", subSlice) }
6. 遍历切片
使用 for
循环
package main import "fmt" func main() { slice := []int{1, 2, 3, 4, 5} for i := 0; i < len(slice); i++ { fmt.Println(slice[i]) } }
使用 range
关键字
package main import "fmt" func main() { slice := []int{1, 2, 3, 4, 5} for index, value := range slice { fmt.Printf("Index: %d, Value: %d\n", index, value) } }
7. 切片的拷贝
使用 copy
函数
package main import "fmt" func main() { source := []int{1, 2, 3} destination := make([]int, len(source)) copy(destination, source) fmt.Println("Destination slice:", destination) }
8. 删除切片中的元素
删除切片中的元素通常需要使用切片的切片操作。
删除第 i 个元素
package main import "fmt" func main() { slice := []int{1, 2, 3, 4, 5} i := 2 slice = append(slice[:i], slice[i+1:]...) fmt.Println("After deletion:", slice) }
删除开头的元素
package main import "fmt" func main() { slice := []int{1, 2, 3, 4, 5} slice = slice[1:] fmt.Println("After deleting first element:", slice) }
删除末尾的元素
package main import "fmt" func main() { slice := []int{1, 2, 3, 4, 5} slice = slice[:len(slice)-1] fmt.Println("After deleting last element:", slice) }
9. 多维切片
定义和初始化多维切片
package main import "fmt" func main() { matrix := [][]int{ {1, 2, 3}, {4, 5, 6}, } fmt.Println("2D Slice:", matrix) }
遍历多维切片
package main import "fmt" func main() { matrix := [][]int{ {1, 2, 3}, {4, 5, 6}, } for i := 0; i < len(matrix); i++ { for j := 0; j < len(matrix[i]); j++ { fmt.Printf("matrix[%d][%d] = %d\n", i, j, matrix[i][j]) } } }
切片操作的总结
切片是 Go 语言中处理动态数组的核心工具,提供了比数组更为灵活和高效的操作方式。通过掌握切片的创建、访问、修改、遍历、动态增长、拷贝和删除等操作,开发者可以更高效地处理各种数据集合。
使用切片时需要注意以下几个重要点,以避免潜在的问题并提高代码的性能和可读性。
1. 切片的零值
切片的零值是 nil
,一个 nil
切片的长度和容量都是 0。对一个 nil
切片进行追加操作是安全的。
package main
import "fmt"
func main() {
var slice []int
fmt.Println(slice == nil) // 输出:true
slice = append(slice, 1, 2, 3)
fmt.Println(slice) // 输出:[1 2 3]
}
package main
import "fmt"
func main() {
var slice []int
fmt.Println(slice == nil) // 输出:true
slice = append(slice, 1, 2, 3)
fmt.Println(slice) // 输出:[1 2 3]
}
package main
import "fmt"
func main() {
slice := make([]int, 3, 5)
fmt.Println("Length:", len(slice)) // 输出:3
fmt.Println("Capacity:", cap(slice)) // 输出:5
}
3. 切片的共享底层数组
多个切片可以共享同一个底层数组。对一个切片的修改可能影响其他切片,因为它们引用的是同一个底层数组。
package main
import "fmt"
func main() {
arr := [5]int{1, 2, 3, 4, 5}
slice1 := arr[:]
slice2 := arr[2:]
slice2[0] = 100
fmt.Println(slice1) // 输出:[1 2 100 4 5]
fmt.Println(slice2) // 输出:[100 4 5]
}
4. 使用 append
时的注意事项
使用 append
向切片添加元素时,如果底层数组的容量不足以容纳新的元素,append
会创建一个新的数组并将旧数组的内容复制过去。因此,可能会发生切片的重新分配和复制操作。
package main
import "fmt"
func main() {
slice := make([]int, 3, 5)
fmt.Println("Before append: len =", len(slice), ", cap =", cap(slice))
slice = append(slice, 4, 5, 6)
fmt.Println("After append: len =", len(slice), ", cap =", cap(slice))
}
5. 切片的切片操作
对切片进行切片操作时,新切片仍然引用相同的底层数组。修改新切片的元素会影响原始切片。
package main
import "fmt"
func main() {
slice := []int{1, 2, 3, 4, 5}
subSlice := slice[1:4]
subSlice[0] = 100
fmt.Println(slice) // 输出:[1 100 3 4 5]
fmt.Println(subSlice) // 输出:[100 3 4]
}
6. 切片的深拷贝
如果需要创建一个切片的深拷贝,即独立于原始切片的一个新切片,可以使用 copy
函数。
package main
import "fmt"
func main() {
source := []int{1, 2, 3}
destination := make([]int, len(source))
copy(destination, source)
destination[0] = 100
fmt.Println("Source slice:", source) // 输出:[1 2 3]
fmt.Println("Destination slice:", destination) // 输出:[100 2 3]
}
7. 切片的内存管理
切片在处理大量数据时可能会导致内存泄漏,因为切片引用的底层数组不会被垃圾回收,即使切片本身已经不再使用。解决方法是明确切片和底层数组的关系,必要时手动释放不再使用的切片。
package main
import "fmt"
func main() {
original := make([]int, 1000)
// 创建一个小切片引用大数组
smallSlice := original[:10]
// 为了避免内存泄漏,可以创建一个新的切片并复制所需的部分
copySmallSlice := make([]int, len(smallSlice))
copy(copySmallSlice, smallSlice)
fmt.Println("New small slice:", copySmallSlice)
}
8. 切片的边界检查
Go 语言会自动进行边界检查,以确保切片操作不会超出其范围。这种检查在运行时进行,如果访问越界,将会引发运行时恐慌(panic)。
package main
import "fmt"
func main() {
slice := []int{1, 2, 3}
fmt.Println(slice[2]) // 正常访问
// fmt.Println(slice[3]) // 运行时恐慌:index out of range
}
9. 切片的零值行为
切片的零值行为是合理的,这意味着即使切片为 nil
,也可以安全地对其执行操作,如追加元素或遍历。
package main
import "fmt"
func main() {
var slice []int
fmt.Println("Length:", len(slice)) // 输出:0
fmt.Println("Capacity:", cap(slice)) // 输出:0
slice = append(slice, 1, 2, 3)
fmt.Println("After append:", slice) // 输出:[1 2 3]
}