August Rush

一个还在努力成长的小火汁!

游龙当归海,海不迎我自来也。

We create our own demons.

You can reach me at augustrush0923@gmail.com
Golang中使用切片遇到的一些坑
发布:2024年01月07日 | 作者:augustrush | 阅读量: 223

切片(slice)

首先再来回顾一下Golang中的切片(Slice),Golang中的切片是一种动态数组 ,它是对数组的抽象。切片的定义可以用如下公司表示:

切片 = 数组 + 指针

也就是说, 切片本身没有任何数据,它只是对一个底层数组的引用。同时它还维护了两个值,长度容量长度表示切片中有效元素的数量,容量表示切片可以容纳的最大元素数量。

再来回顾一下数组的内存管理

  • 数组的内存是连续的
  • 数组的内存地址实际上是数组内第一个元素的内存地址

所以可以总结出切片的存储结构如下图:

切片的结构

切片的自动扩容机制

切片的长度表示切片中有效元素的数量,容量表示切片可以容纳的最大元素数量。当切片的长度达到容量时,切片就会自动扩容。

因为切片本身不存储数据,当容量不足以装下新元素时,Golang会把当前切片指向的数组进行拷贝并连同需要新增的元素添加到一个新创建的数组中以实现扩容。

func main(){
  // 定义一个切片并初始化两个数据
    slice := []int{1, 2}
    fmt.Printf("len: %v, cap: %v\n", len(slice), cap(slice))
    // len: 2, cap: 2

    // 新增数据
    slice = append(slice, 3)
    fmt.Printf("len: %v, cap: %v\n", len(slice), cap(slice))
  // len: 3, cap: 4
}

遇到的问题

删除切片中的数据

因为Golang没有提供删除切片的内置函数,但是通过append函数也可以实现删除的效果。

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

最后可以发现slice中的第二个数据已经删除掉了。

因为append函数返回的切片与原切片指向同一块内存空间,索引原切片数据也会受到影响。

func main() {
    slice := []int{1, 2, 3}
    newSlice := append(slice[:1], slice[2:]...)
  fmt.Println(slice)    // [1, 3, 3]
  fmt.Println(newSlice) // [1, 3]
}

看一下图解:

上述问题的变种

再来看一段代码:

func main() {
    type Map map[string][]int
    m := make(Map)
    s := []int{1, 2}
    s = append(s, 3)
    fmt.Printf("%+v\n", s)
    m["a"] = s
    s = append(s[:1], s[2:]...)
    fmt.Printf("%+v\n", s)
    fmt.Printf("%+v\n", m["a"])
}

其输出结果:

[1 2 3]
[1 3]
[1 3 3]

刚开始我也很疑惑为什么结果会是[1 3 3],后来结合切片和数组的定义才恍然大悟。

先看一下图解:

图解

再来逐行解释一下:

s := []int{1, 2}

首先创建了一个切片,其长度为2,容量为2,数组内的数据为{1, 2}

s = append(s, 3)

当新增数据的时候,因为切片长度不够,触发了自动扩容。扩容至容量的二倍。并生成一个新的数组,其长度为3,容量为4,数组内的数据为{1, 2, 3}

m["a"] = s

将map类型的键a的值设为s。此时会新建一个地址存放这个切片,这个切片的长度为3,容量为4,数组内的数据为{1, 2, 3}

s = append(s[:1], s[2:]...)

此步骤其实就是对切片中下标为1的数据进行删除。因为没进行扩容操作,所以还是在原先的数组中进行操作。

因为对变量s进行了重新赋值,所以它指向的切片长度为2, 容量为4,数组内的数据为{1, 3}

m["a"])

因为上一步中,对原切片进行了操作。m["a"]因为使用其引用,所以受到了影响。它指向的切片长度为3,容量为4,数组内的数据变为{1, 3, 3}



  • 标签云

  • 支付宝扫码支持一下

  • 微信扫码支持一下



基于Nginx+Supervisord+uWSGI+Django1.11.1+Python3.6.5构建

京ICP备20007446号-1 & 豫公网安备 41100202000460号

网站地图 & RSS | Feed