生活中,打造完美的家庭聚餐
假设你在准备一个周末家庭聚餐,想要为家人做一道美味的菜。这个时候,可以将Golang的切片概念与食材选择相结合。
-
切片的定义: 就像你在做菜时选择合适的食材一样,Golang中的切片就像一个动态数组,允许你根据需要动态地调整大小。
-
创建切片: 想象你在准备沙拉,初始时可能有很多蔬菜,但你不确定需要多少。Golang切片的创建方式也是类似的,不需要预先指定大小。
gosalad := []string{"lettuce", "tomato", "cucumber", "carrot"}
-
切片的追加和删除: 当你决定在沙拉中加入一种新的食材,或者有人不喜欢某一种,你可以随时追加或删除。
go// 追加新食材 salad = append(salad, "bell pepper") // 删除不喜欢的食材 salad = append(salad[:2], salad[3:]...)
-
切片的动态性: 正如你在调整菜谱时可以随时添加或去除食材,Golang切片的动态性允许你在程序运行时动态调整大小,适应不同的需求。
-
共享底层数组: 多个人共享同一盘沙拉,类似地,多个切片可以共享相同的底层数组。如果你在切片中添加或删除元素,底层数组会被修改,影响到所有共享的切片。
通过这个生活中的比喻,希望你可以更好地理解Golang中切片的概念,以及它如何在程序中灵活地处理数据。
Golang中的切片
切片(Slice)是Go语言中一个重要而强大的数据结构,用于处理序列化的数据集合。切片提供了对数组的抽象,使得在处理集合数据时更加灵活和便捷。以下是关于切片的核心概念:
-
定义: 切片是对数组的一层封装,它指向数组的某个部分,并且包含了三个重要的属性:指针、长度和容量。
-
指针(Pointer): 切片包含一个指针,指向数组中的第一个元素。这意味着切片的起始位置是数组的某个位置。
-
长度(Length): 切片的长度表示它包含的元素数量,即切片的实际大小。
-
容量(Capacity): 切片的容量是指从切片的起始位置到底层数组末尾的元素数量。容量决定了切片的最大大小。
-
创建切片: 可以使用切片表达式创建一个切片,例如
arr[low:high]
,其中low
是起始索引,high
是结束索引(不包含)。goarr := []int{1, 2, 3, 4, 5} slice := arr[1:4] // 创建一个包含 arr[1], arr[2], arr[3] 的切片
-
切片的动态性: 切片是动态的,可以在运行时修改长度,从而实现动态数组的效果。
goslice = append(slice, 6) // 在切片末尾添加元素
-
共享底层数组: 多个切片可以共享相同的底层数组,一个切片的修改会影响到其他共享底层数组的切片。
goarr := []int{1, 2, 3, 4, 5} slice1 := arr[1:4] slice2 := arr[2:5] slice1[0] = 99 fmt.Println(arr) // 输出 [1 99 3 4 5]
切片的灵活性使得在处理数据集时更加方便,同时由于切片是引用类型,它的传递不会复制底层数组,减少了内存开销。切片在Go语言中广泛用于处理动态数据集,如切片、映射等。
实践
这段Go代码演示了切片(slice)的基本操作和特性。以下是对代码的解释:
-
未初始化的切片:
govar s []string fmt.Println("uninit:", s, s == nil, len(s) == 0)
创建一个未初始化的切片
s
,打印切片内容、是否为nil
以及长度是否为0。因为切片未初始化,所以内容为空,判断为nil
,长度为0。 -
使用 make 创建切片:
gos = make([]string, 3) fmt.Println("emp:", s, "len:", len(s), "cap:", cap(s))
使用
make
函数创建一个长度为3的切片s
,打印切片内容、长度和容量。此时切片内容为零值(字符串的零值为""),长度为3,容量也为3。 -
切片赋值和访问:
gos[0] = "a" s[1] = "b" s[2] = "c" fmt.Println("set:", s) fmt.Println("get:", s[2])
对切片进行赋值和访问。切片的索引从0开始,通过索引访问和设置元素。
-
使用 append 添加元素:
gos = append(s, "d") s = append(s, "e", "f") fmt.Println("apd:", s)
使用
append
函数向切片追加元素。append
函数可以一次追加多个元素。 -
复制切片:
goc := make([]string, len(s)) copy(c, s) fmt.Println("cpy:", c)
使用
make
创建一个与切片s
相同长度的新切片c
,然后使用copy
函数将s
复制到c
。 -
切片操作:
gol := s[2:5] fmt.Println("sl1:", l) l = s[:5] fmt.Println("sl2:", l) l = s[2:] fmt.Println("sl3:", l)
演示切片操作,包括从索引2截取到索引5,从头截取到索引5,从索引2截取到末尾。
-
切片的声明和比较:
got := []string{"g", "h", "i"} fmt.Println("dcl:", t) t2 := []string{"g", "h", "i"} if slices.Equal(t, t2) { fmt.Println("t == t2") }
使用简短声明语法创建切片
t
,并比较两个切片是否相等。 -
二维切片:
gotwoD := make([][]int, 3) for i := 0; i < 3; i++ { innerLen := i + 1 twoD[i] = make([]int, innerLen) for j := 0; j < innerLen; j++ { twoD[i][j] = i + j fmt.Printf("i=%d j=%d\n", i, j) } } fmt.Println("2d:", twoD)
创建一个二维切片
twoD
,演示如何动态创建不同长度的切片,并为其赋值。最后打印二维切片的内容。完整示例代码:
go
package main
import (
"fmt"
"golang.org/x/exp/slices"
)
func main() {
var s []string
fmt.Println("uninit:", s, s == nil, len(s) == 0)
s = make([]string, 3)
fmt.Println("emp:", s, "len:", len(s), "cap:", cap(s))
s[0] = "a"
s[1] = "b"
s[2] = "c"
fmt.Println("set:", s)
fmt.Println("get:", s[2])
fmt.Println("len:", len(s))
s = append(s, "d")
s = append(s, "e", "f")
fmt.Println("apd:", s)
c := make([]string, len(s))
copy(c, s)
fmt.Println("cpy:", c)
l := s[2:5]
fmt.Println("sl1:", l)
l = s[:5]
fmt.Println("sl2:", l)
l = s[2:]
fmt.Println("sl3:", l)
t := []string{"g", "h", "i"}
fmt.Println("dcl:", t)
t2 := []string{"g", "h", "i"}
if slices.Equal(t, t2) {
fmt.Println("t == t2")
}
twoD := make([][]int, 3)
for i := 0; i < 3; i++ {
innerLen := i + 1
twoD[i] = make([]int, innerLen)
for j := 0; j < innerLen; j++ {
twoD[i][j] = i + j
fmt.Printf("i=%d j=%d\n", i, j)
}
}
fmt.Println("2d:", twoD)
}