数组与切片1.数组与切片的相同以及不同点
它们都属于集合类型,它们的值都可以用来存储某一种类型的值,但是数组类型的值的长度是固定的,而切片类型的值是可变长的。数组的长度在声明它的时候必须给定并且以后不会再改变,数组的长度是其类型的一部分[1]string 和[2]string就是两个不同的数组类型;而切片的类型字面量中只有元素类型,切片的长度随着元素的增加而增加,但不会随着元素的减少而减少。可以把切片看做数组的一层简单的封装,因为切片的底层数据结构中是一个数组,而切片可以看做对数组某个连续片段的引用。
所以Go语言的切片类型属于引用类型,同属引用类型的还有map类型,chan类型,func类型;而Go语言的数组类型则属于值类型,同属值类型的还有基础数据类型以及结构体类型,这里有不同的不必担心,后续都会讲到。
通过内建函数len,可以的到它们的长度。通过内建函数cap,可以得到它们的容量。数组的容量大于其长度,而且是不可变得。而切片的容量的变化是有规律的。
2.怎样正确估算切片的长度和容量
例子:
package main
import ("fmt")
func main() {a := make([]int, 5)fmt.Println(len(a))fmt.Println(cap(a))fmt.Println(a)b := make([]int, 5, 8)fmt.Println(len(b))fmt.Println(cap(b))fmt.Println(b)}切片a和切片b的容量分别是5,8
问题解析:
用make初始化切片时,如果不指明容量,那么它就会和长度一致,如果指明了容量那么切片的实际容量就是指明的容量。
容量就相当于底层数组的长度。而长度就相当于一个窗口,一个能看到底层数组一部分连续元素的窗口,例如b的长度为5,那切片b能够看到底层数组的[0,4]的元素,现在切片b的最左边的元素就相当于底层数组的最左边元素。
但是通过切片表达式基于某个数组或切片生成新切片的时候,就不一样了。
例子:c := []int{1, 2, 3, 4, 5, 6, 7, 8}d := c[3:6]fmt.Println(len(c))fmt.Println(cap(c))fmt.Println(c)fmt.Println(len(d))fmt.Println(cap(d))fmt.Println(d)
此时c 的长度容量都为8但是d的长度为3容量为5解析,首先长度很容易理解,它们有多少个元素长度即为多少。在前面说过切片的容量代表了底层数组的长度,但这仅限于使用make函数或者切片值字面量初始化切片的情况。更通用的规则是:一个切片的容量可以看作是透过这个窗口最多可以看到底层数组中元素的个数。由于s4是在s3上施加切片操作的来的,所以s3的底层数组就是s4的底层数组。又因为在底层数组不变的情况下,切片代表的窗口可以向右扩展,一直到数组的末尾,所以s4的容量就是其底层数组的长度8减去上述切片表达式中的那个起始索引3,所以d的容量是5,可以通过d[0:cap[d]]的方式向右扩展最大化.
3.怎样估算切片容量的增长
一旦一个切片无法容纳更多元素,Go语言就会想办法扩容。但它不会改变原来的切片,而是会生成一个更大容量的切片,然后将原有元素和新元素一起拷贝到新切片中。
一般情况下,新切片容量将会是原切片容量的2倍,但是原切片长度大于1024时,Go语言将会以原容量的1.25倍作为新容量的基准。如果我们一次追加的元素大于了原容量的两倍,那么新容量就会以新长度为基准。
4.切片的底层数组何时被替换
一个切片的底层数组永远不会被替换。切片在扩容时Go语言会生成新的底层数组,但它也同时生成了新的切片。它把新的切片作为新底层数组的窗口,对原切片的底层数组不会做任何改动,只要新长度不会超过切片的容量,append就不会引起扩容。只会替换底层数组中紧临切片窗口后边的元素,不扩容代表着append返回的是指向原底层数组的新切片,扩容时才会返回指向新底层数组的新切片。
注意,当两个切片指向的同一个底层数组时,当其中一个切片扩容后,两者指向的就不是同一个底层数组了。同时当两个切片指向同一个底层数组时,改变其中一个切片的值会影响到另一个切片的值,这种问题可以通过copy函数来解决
如果觉得《go语言学习笔记(5)数组与切片》对你有帮助,请点赞、收藏,并留下你的观点哦!