数组和切片

对比学习数组和切片

数组的长度是固定的,切片是可变长的。编程

数组的类型字面量中必须有元素的类型和长度。数组的长度在声明的时候必须给定,而且以后不会再改变,数组的长度是其类型的一部分。好比[1]string[2]string就是2个不一样的数组类型。数组

切片的类型字面量中只有元素的类型,而没有长度。切片的长度能够自动地随着其中元素数量的增加而增加,但不会随着元素数量的减小而减少。数据结构

能够把切片看做是对数组的一层简单封装,由于在每一个切片额底层数据结构中,必定会包含一个数组。数组能够叫作切片的底层数组,而切片能够看作是对数组的某个连续片断的引用。app

正由于“切片能够看做是数组的某个连续片断的引用”,Go语言的切片类型属于引用类型。同属于引用类型的还有字典类型,通道类型,函数类型等;而Go语言的数组类型则属于值类型,同属于值类型的还有基础数据类型以及结构体类型。编程语言

Go语言里不存在像Java等编程语言中使人困惑的“传值或传引用”问题。在Go语言中,咱们判断所谓的“传值”或者“传引用”只要看传递的值的类型就行了,若是传递的值的类型是值类型,那么就是“传值”,若是传递的值的类型是引用类型的,那么就是“传引用”。从传递成本的角度讲,引用类型的值每每要比值类型的值低不少。函数

能够在数组和切片之上应用“索引表达式”,获得的是某个元素。 也能够在数组和切片上应用“切片表达式”,获得的是一个新的切片。学习

调用内建函数len,能够获得数组和切片的长度。数组的容量永远等于其长度,都是不可变的。 调用内建函数cap,能够获得数组和切片的容量。切片的容量是可变的,变化也是有规律可循的。code

怎样正确估算切片的长度和容量?

内建函数make能够建立切片。 make([]int,5,8)第一个参数[]int指明切片的类型,第二个参数5指明切片的长度,第三个参数8指明切片的容量。索引

切片的容量是什么意思呢? 还记得上面说的吗:数组是切片的底层数据结构的一部分。切片的容量实际上表明了它的底层数组的长度。 在切片s1上应用切片表达式,获得的新的切片s2的底层数组和s1的底层数组是同样的。string

回答问题:怎样估算切片容量的增加?

一旦一个切片没法容纳更多的元素,Go语言就会想办法扩容。可是它并不会改变原来的切片,而是会生成一个容量更大的切片,而后把原有的元素和新元素一并拷贝到新的切片总。在通常状况下,能够简单地认为新切片的容量(简称新容量)将会是原切片容量(简称原容量)的2倍。

可是当原切片的长度(原长度)大于或等于1024时,Go语言将会以原容量的1.25倍做为新容量的基准(新容量基准)。新容量基准将会被调整(不断地与1.25相乘),直到结果不小于原长度与要追加的元素数量之和(简称新长度)。最终,新容量每每会比新长度大一些,固然,相等也是可能的。

另外,若是咱们一次追加的元素过多,以致于新长度比原容量的2倍还要大,那么新容量就会以新长度为基准。与前面的状况同样,最终的容量在不少时候都要比新容量基准更大一些。

问题2:切片的底层数组何时会被替换?

确切的说,一个切片的底层数组永远不会被替换。为何?虽然在扩容的时候,Go语言必定会生成新的底层数组,可是它同时也生成了新的切片。它是把新的切片做为了新底层数组的窗口,而没有对原切片及其底层数组作任何改动。

请记住,在无需扩容的时候,append函数返回的是指向原底层数组的新切片,而在须要扩容的时候,append函数返回的是指向新底层数组的新切片。因此,严格来说,“扩容”这个词用在这里虽然形象可是不合适。

只要新长度不会超过原切片的原容量,那么使用append函数对其追加元素的时候就不会引发扩容。这只会使紧邻切片窗口右边(底层数组中的)元素被新的元素替换掉。

问题3:若是有多个切片指向了同一个底层数组,应该注意什么?

初始时两个切片引用同一个底层数组,在后续操做中对某个切片的操做超出底层数组的容量时,这两个切片引用的就不是同一个数组了

s1 :=[]int{1,2,3,4} s2 := s1[0:4] 就像这样,这样的话改变s2会影响s1,如何消除这种影响呢

能够用copy函数,或者本身深拷贝。

问题4:怎样援用“扩容”的思想对切片进行“缩容”?

切片缩容以后仍是会引用底层的原数组,这有时候会形成大量缩容以后的多余内容没有被垃圾回收。可使用新建一个数组而后copy的方式。

相关文章
相关标签/搜索