首先再来回顾一下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构建