【GoLand】切片的理解和应用

203 阅读3分钟

一、切片的基本使用

1.直接声明创建切片

  • (1)直接进行append追加操作
  • (2)对数组进行截取
package main

import "fmt"

func main() {
    var slice []int
    var array [5]int = [5]int{2,2,2,2,2}
    slice = append(slice, 1, 2, 3)
    fmt.Println(slice)
    slice = array[:len(array)]
    fmt.Println(slice)
}

2.使用make创建切片

  • (1)预留了内存,可以直接使用索引赋值和访问
  • (2)也可以使用append进行元素追加
package main

import "fmt"

func main() {
    slice := make([]int, 5)
    slice = append(slice, 6)
    slice[0] = 100
    fmt.Println(slice)
}

3.对切片进行增删改查

(1)append的用法
package main

import "fmt"

func main() {
    slice := make([]int, 5)
    addend := make([]int, 10)
    addend[8] = 800
    // 1.追加单个元素
    slice = append(slice, 6)
    // 2.追加多个元素
    slice = append(slice, 6, 7)
    // 3.追加切片
    slice = append(slice, addend ...)
    // 4.追加临时切片
    slice = append([]int{999,999}, slice ...)
    fmt.Println(slice)
}

(2)copy的用法

  • (1)多的拷贝到少的,会按照少的最大值拷贝
  • (2)少的拷贝多的,会覆盖少的最大值数目的元素
package main

import "fmt"

func main() {
    function1()
    function2()
}

func function1 () {
    var slice1 []int = []int{1, 2, 3}
    var slice2 []int = []int{4, 5, 6, 7}
    copy(slice1, slice2)
    fmt.Println(slice1)
    fmt.Println(slice2)
}

func function2 () {
    var slice1 []int = []int{1, 2, 3}
    var slice2 []int = []int{4, 5, 6, 7}
    copy(slice2, slice1)
    fmt.Println(slice1)
    fmt.Println(slice2)
}

二、切片底层数据结构

1.切片的底层数据结构由三段数据组成:

  • (1)指向数据头部的指针
  • (2)切片的长度
  • (3)切片的容量

2.切片上下界索引变化时内存变化原理

三、切片易错点

1.切片的内存增长的本质

1.内存拷贝
将源切片的内容拷贝到目的切片的内容
2.内存扩容
(1)扩容策略
a)如果cap<1024,按照每次2倍增长,否则按照cap1/4增长
b)扩容是根据切片容量来的,而不是数组长度来的
(2)扩容之后是生成新数组还是复用老数组
a)如果切片append追加之后的总容量还是小于复用数组的长度,那就复用老数组
b)如果切片append追加之后的总容量大于数组的长度,那就会开辟新的空间,建立全新的数组,返回相应的切片引用

如下的例子可以说明扩容策略对切片的影响,在实际编码中应该注意多切片底层是共享数组的,改变一个切片的值,可能影响了其他多个切片:

2.切片作为函数入参是值传递,但是函数内部修改却能引起外部数据变化

  • 1.切片作为函数入参传递的实质是值传递,但这点通过函数内外参数地址不一致可以确定
  • 2.函数内外变化同步是因为切片底层数据指针进行了拷贝,是直接操作指针修改对应内存里的值,变相成为了地址传递
package main

import "fmt"

func changeSlice(s []int) {
    fmt.Printf("func: %p \n", &s)
    s[1] = 111
}

func main() {
    slice := []int{0, 1, 2, 3}
    fmt.Printf("slice: %v slice addr %p \n", slice, &slice)

    changeSlice(slice)
    fmt.Printf("slice: %v slice addr %p \n", slice, &slice)
}

3.切片初始化

切片使用make创建时,会自动给前len个元素赋值为0,append是基于len继续添加的,而不是基于0开始添加的

package main

import "fmt"

func main() {
    function1()
    function2()
}

func function1() {
    s := make([]int, 5)
    s = append(s, 1, 2, 3)
    fmt.Println(s)
}

func function2() {
    s := make([]int, 0)
    s = append(s, 1, 2, 3)
    fmt.Println(s)
}

4.切片截取与切片长度

(1)切片截取后,len会发生变化,可以通过len直接获取到长度
(2)切片截取使用[0:len]是获取当前所有元素,[0:len-1]才是获取除最后一个外的元素,右边的区间为开区间
package main

import "fmt"

func main() {
    var arr []int = []int{1, 2, 3, 4}
    fmt.Println(len(arr))
    arr = arr[0 : len(arr) - 1]
    fmt.Println(len(arr))
}

四、参考资料

juejin.cn/post/688811…