最近起步学习golang,总结一下golang中切片和数组的关系和不同
1. 数组:
struct
1. 数组为值类型,换言之用数组给变量赋值,是将数组中所有元素拷贝一份
2. 数组作为函数参数传递为值传递,而不是指针
3. 数组的长度不可变,即不可进行扩容和缩容
数组的初始化:
//初始长度为5的数组
[5] int {1,2,3,4,5}
//用...代替初始长度,其长度是根据初始化时指定的元素个数决定的,本例中为5
[...] int {1,2,3,4,5}
//在初始化元素中可以指定特定下标的起始值,未指定的用0填充
//本例中数组为:[0,0,1,0,3]
[...] int {2:1,4:3}
2. 切片:
数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型Slices切片(“动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。切片中有两个概念:一是len长度,二是cap容量,长度是指已经被赋过值的最大下标+1,可通过内置函数len()获得。容量是指切片目前可容纳的最多元素个数,可通过内置函数cap()获得。切片是引用类型,因此在当传递切片时将引用同一指针,修改值将会影响其他的对象,但是这种情况也有一个例外,详见下面扩容部分。
其结构如下:
其数据结构分为3个部分:
1. 地址指针:记录slice首个元素的地址
2. 长度:当前slice中存了多少个数据
3. 容量,当前slice中一共可以存多少数据(当容量不足的时候,会触发扩容机制)
切片的初始化:
//注意中括号中不可以有值,否则初始化的结果为数组
s := [] int {1,2,3 }
// : 的使用
s := arr[:] //arr的全部元素
s := arr[startIndex:endIndex] //arr的startIndex到endIndex-1
s := arr[startIndex:] //缺省endIndex时将表示一直到arr的最后一个元素
s := arr[:endIndex] //缺省startIndex时将表示从arr的第一个元素开始
s1 := s[startIndex:endIndex] //也可以用切片初始化切片
// 通过内置函数make()初始化切片s,[]int 标识为其元素类型为int的切片
s := make([]int,len,cap)
切片的引用机制
如下图
当对slice或者array用冒号进行切片的时候,切片的长度会根据:左右的值进行自行运算,而容量为切片起始点到原数组/切片的结束结束点。
由于是引用类型,子切片值的改变也会影响到原数组/切片的值
import (
"fmt"
)
func main(){
arr := [4]int{10, 20, 30, 40}
slice := arr[0:2]
slice[0] = 1
// 在触发扩容之前调用append,也会影响原数组的值
slice = append(slice, 99)
fmt.Println(slice)
fmt.Println(arr)
}
程序结果:
[ 1 20 99 ]
[ 1 20 99 40 ]
slice
请记住以下两条规则:
- 如果切片的容量小于1024个元素,那么扩容的时候slice的cap就翻番,乘以2;一旦元素个数超过1024个元素,增长因子就变成1.25,即每次增加原来容量的四分之一。
- 如果扩容之后,还没有触及原数组的容量,那么,切片中的指针指向的位置,就还是原数组,如果扩容之后,超过了原数组的容量,那么,Go就会开辟一块新的内存,把原来的值拷贝过来,这种情况丝毫不会影响到原数组。
知道了一下规则,请看下面程序,试问输出结果:
import (
"fmt"
)
func main(){
arr := [4]int{10, 20, 30, 40}
slice := arr[0:2]
testSlice1 := slice
testSlice2 := append(append(append(slice, 1),2),3)
slice[0] = 11
fmt.Println(testSlice1[0])
fmt.Println(testSlice2[0])
}
由于append了3次而slice中的cap=4,则会触发扩容,因此testSlice2和slice并不是同一个slice,改变其中之一另一个不会收到影响
程序结果:
11
10