Slice 简介

  • 切片是一个拥有相同类型元素可变长度的集合,数组是不可变长的集合。切片相对数组来说更加的灵活,可追加元素,自动扩容。
  • 它可理解为可变长度的数组,可以看作动态“数组”。
  • 包含三个字段。如图所示:
    image.png
    这3个字段分别是指向底层数组的指针、切片访问的元素的个数(即长度)和切片允许增长到的元素个数(即容量)。

    Slice 切片的定义与初始化

    // 方法一:使用var关键字声明
    var sliceExample []string
    
    // 方法二:使用make方法
    var sliceExample = make([]string, 1)
    
    // 方法三:方法二的简化
    sliceExample := make([]string, 1)
    
    // 方法四:直接初始化
    var sliceExample = []string{"1", "2", "3"}
    
    // 方法五:方法四简化
    sliceExample := []string{"1", "2", "3"}

    初始化时给出所需的长度和容量作为索引,最后一个元素设置为指定的元素,如下所示:

    //创建11个元素的切片,第11个元素值为:第十一个元素
    sliceExample := []string{10: "第十一个元素"}
    
    fmt.Println("值:", sliceExample, "长度len:", len(sliceExample), "容量cap:", cap(sliceExample))

    image.png

注意: GoLang不允许定义容量小于长度的切片

// 初始化一个切片
sliceExample := make([]int, 61, 5)

image.png

使用数组初始化切片

// 创建数组
var arr = [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
// 从索引1(包含索引1)的位置开始,到索引3(不包含索引3)
var sliceZero = arr[1:3]
// 从索引1(包含索引1)的位置开始,到尾部所有,相当于删除索引为1的元素
var sliceOne = arr[1:]
// 从索引0(包含索引0)的位置开始,到索引2(不包含2)结束
var sliceTwo = arr[:2]
// 把arr所有元素都赋值给sliceThree
var sliceThree = arr[:]
// 从索引0(包含索引0)的位置开始,到arr长度-1的位置结束, 相当于删除最后一个元素
var sliceFour = arr[:len(arr)-1]

fmt.Println("sliceZero:  ", sliceZero)
fmt.Println("sliceOne:   ", sliceOne)
fmt.Println("sliceTwo:   ", sliceTwo)
fmt.Println("sliceThree: ", sliceThree)
fmt.Println("sliceFour:  ", sliceFour)

// 修改 sliceFour[0] 元素值
sliceFour[0] = 100
fmt.Println(sliceFour)

fmt.Println("sliceZero:  ", sliceZero)
fmt.Println("sliceOne:   ", sliceOne)
fmt.Println("sliceTwo:   ", sliceTwo)
fmt.Println("sliceThree: ", sliceThree)
fmt.Println("sliceFour:  ", sliceFour)

image.png
从上面可以看到, 修改了 sliceFour[0] 的元素值,sliceTwo,sliceThree,sliceFour值也跟着修改了;我们看一个更具体的例子:

var a = []int{1, 3, 4, 5}
fmt.Printf("slice a : %v , len(a) : %v\n", a, len(a))
b := a[1:2]
fmt.Printf("slice b : %v , len(b) : %v\n", b, len(b))
c := b[0:3]
fmt.Printf("slice c : %v , len(c) : %v\n", c, len(c))

image.png
可能会好奇为什么是这样? 原因是上面操作的为底层a, 我们看一下这张图:
image.png

nil和空切片

  • nil切片
    创建nil切片, 只需要在声明切片的时候不初始化就可以了,如下所示:

    // 创建nil切片
    var nilSlice []int
    
    if nilSlice == nil {
      fmt.Println("nilSlice为一个nil切片")
    }

    image.png

使用场景:
(1). 函数要求返回一个切片但是发生异常的时候;
(2). 描述一个不存在的切片时,nil切片会很好用;
(3). nil 切片可以用于很多标准库和内置函数

nil切片的状态:
image.png

  • 空切片
    方法1:

    // 创建空切片
    emptySlice := []int{}
    
    if emptySlice == nil {
      fmt.Println("emptySlice为一个nil切片")
    } else if len(emptySlice) == 0 {
      fmt.Println("emptySlice是一个空切片: len", len(emptySlice), "cap:", cap(emptySlice))
    }

    方法2:

    // 创建空切片
    emptySlice := make([]int, 0)
    
    if emptySlice == nil {
      fmt.Println("emptySlice为一个nil切片")
    } else if len(emptySlice) == 0 {
      fmt.Println("emptySlice是一个空切片: len", len(emptySlice), "cap:", cap(emptySlice))
    }

    image.png

空切片的状态:
image.png
不管是使用 nil 切片还是空切片,对其调用内置函数 append()、len() 和 cap() 的效果都是一样的。

获取切片的长度和容量

len():当前切片个有多少个元素
cap():底层元素个数,通俗得讲:切片总共能存放多少个元素

// 初始化一个切片
sliceExample := make([]string, 0, 10)

fmt.Println("填充之前的切片", sliceExample, "length:", len(sliceExample), "cap", cap(sliceExample))

image.png

切片的赋值

  • 初始化一个nil切片,使用下标赋值时会报错(panic: runtime error: index out of range [0] with length 0),

    // 创建一个nil切片
    var emptySlice []int
    // 覆盖下标为0的元素值
    emptySlice[0] = 26
    
    fmt.Println(emptySlice)

    image.png
    注意:此时使用 emptySlice = append(emptySlice, 1) 添加元素

  • 使用下标覆盖切片元素

    // 创建切片,并对其进行初始化
    emptySlice := []int{2, 3, 4, 5}
    
    // 覆盖下标为0的元素值
    emptySlice[0] = 26
    
    fmt.Println(emptySlice)

    image.png

  • 使用append添加元素
    使用append()函数为切片动态添加元素时,如果空间不够容纳足够多的元素,就会进行扩容,此时切片的长度会发生变化。

    // 声明一个切片
    var sliceExample []string
    
    // 单次追加单个元素
    sliceExample = append(sliceExample, "PHP")
    // 单词追加多个元素
    sliceExample = append(sliceExample, "Java", "JavaScript", "Rust")
    
    fmt.Println(sliceExample)

    image.png

我们初始化了一个切片,容量为10,向里面填充11个元素,因为超过了容量,此时切片被扩容了。

// 初始化一个切片
sliceExample := make([]string, 0, 10)

// 填充元素
for i := 0; i <= 10; i++ {
    sliceExample = append(sliceExample, "PHP")
}

fmt.Println("填充之后的切片", sliceExample, "length:", len(sliceExample), "cap:", cap(sliceExample))

image.png

切片读取

使用下标进行访问

// 初始化一个切片
sliceExample := make([]string, 0, 10)

// 填充元素
sliceExample = append(sliceExample, "GoLang")

// 直接使用下标进行访问
fmt.Println(sliceExample[0])

image.png

循环遍历

// 初始化一个切片
sliceExample := make([]string, 0, 10)

// 填充元素
for i := 0; i <= 10; i++ {
    sliceExample = append(sliceExample, "PHP")
}

// 对切片进行遍历
for index, value := range sliceExample {
    fmt.Println("下标:", index, "value:", value)
}

image.png

切片元素的删除

1. 从起始位置开始删除

a = a[N:] // 删除开头N个元素, 这样删除会重置下标

// 初始化一个切片
sliceExample := []int{1, 2}

fmt.Println("删除之前的元素:")
for index, value := range sliceExample {
    fmt.Println("index:", index, "value:", value)
}
// 从切片起始位置删除一个元素
sliceExample = sliceExample[1:]

fmt.Println("删除之后的元素:")
for index, value := range sliceExample {
    fmt.Println("index:", index, "value:", value)
}

image.png

2. 从起始位置开始删除

a = a[:N] // 从尾部删除个元素

// 初始化一个切片
sliceExample := []int{1, 2, 3}

fmt.Println("删除之前的元素:")
for index, value := range sliceExample {
    fmt.Println("index:", index, "value:", value)
}

// 从尾部删除1一个元素
sliceExample = sliceExample[:len(sliceExample)-1]

fmt.Println("删除之后的元素:")
for index, value := range sliceExample {
    fmt.Println("index:", index, "value:", value)
}

image.png

从切片中间删除删除

// 我们初始化一个数组, 然后删除掉中间的6
var arr = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

slice := append(arr[:5], arr[6:]...)

fmt.Println(slice)

image.png

结论:
删除中间1个元素:
slice = append(a[:i], a[i+1:]...)
删除中间N个元素:
slice = append(a[:i], a[i+N:]...)

其他参考资料连接

一起 Go 编程 | 漫谈 Slice 切片
Go语言从切片中删除元素
切片Slice


Architecture
0 声望0 粉丝

知其然, 更要知其所以然~