切片是什么
上篇文章讲过,数组的长度是固定的,切片的长度是可变的。现在我们来看下切片的例子:
func main() {
slice1 := []string{"a", "b", "c"} //[]里没有定义长度
fmt.Println(slice1) //[a b c]
slice2 := make([]int, 3, 5) //使用make初始化一个长度为3的数组切片,元素初始值为整数0,容量为5的切片
fmt.Println(slice2) //[0 0 0]
slice3 := make([]int, 3) //使用make初始化一个长度为3的数组切片,元素初始值为整数0
fmt.Println(slice3) //[0 0 0 0 0]
}
切片扩容
在切片的底层数据结构中会有一个数组,切片可以看作对某个数组的的片段的引用,所以数组是一个值类型的变量,切片是一个引用类型的变量。值类型和引用类型的区别:值类型的变量是直接存放实际的数据,引用类型的变量存放的则是数据的地址,切片的容量就是这个底层数组的长度,那么现在切片需要扩容假如超过了这个底层数组的长度怎么办?
我们先来看看看如何扩容切片,这个操作肯定是非常常见的:
slice4 := append(slice2, 3,4,5,6,7,8,9) //使用append()来扩容 slice2定义了在上面容量只有5,现在加了这么多int类型元素
fmt.Println(slice4) //[0 0 0 3 4 5 6 7 8 9]
假如我们用数组扩容呢:
array1 := [3]string("1","2","3")
array2 := append(array1,"4","5") //会报错, first argument to append must be slice,
fmt.Println(array2)
啊,原来append这个方法专门为切片使用的,数组的所有元素在定义的时候就已经确定了,只能修改,上篇有说,没有给值的默认是为该数组元素类型的默认值。
原来数组这么不中用,感觉啥也干不了,在切片扩容时,超过底层数组容量的时候,底层数组会自动扩容,原有的元素和新元素一并拷贝到新切片,这个切片时基于旧的数组,这个底层数组永远不会变,只是在后面加元素。数组原理扩容属于一个扩展知识,一般会按底层数组的容量1.25倍扩展,如果追加的元素过多,这个倍数是2倍,有兴趣可以百度搜一下扩容原理。
总结
数组的容量永远是它的长度,而切片的容量就是底层数组的长度。切片比数组多了一个容量这个概念,在面试的时候经常会考这个,但实际中在大多数情况下不用关心这个问题,但是用Go语言就必须清楚它所有的设计细节,不然到真实使用的时候展现不出它的优点。