在 golang 文档中,对数组与切片有一些详细的讲解,本文主要讲解数组与切片的关系golang
因为是我的理解,可能有些误差,烦请指正
数组
golang 的数组比较简单,咱们理解几个概念便可数据结构
记住,咱们在此定义了一个 int 类型的数组,长度容量均为 5,在后面的切片讲解中,咱们将对此数组进行切片
app
// 此定义的数组长度为 5 ,那么容量也会固定为 5 arr := [5]int{0, 1, 2, 3, 4} // 数组 p = 0xc00001c0f0,arr = [0 1 2 3 4],len = 5,cap = 5 fmt.Printf("数组 p = %p,arr = %+v,len = %d,cap = %d\n", &arr, arr, len(arr), cap(arr)) // 数组的索引都是从 0 开始的 // 0 1 fmt.Println(arr[0], arr[1])
咱们首先来建立一个对 arr
数组的一个切片code
[1:3] 表示范围从数组的索引 1 至 数组的索引 3,它是不包括 3 的,能够简单理解为 index == 1 && index < 3
当切片生成后,索引默认也是从 0 开始, 切片索引是 [0,1] 对应着数组的索引是 [1,2] 索引
arrSlice := arr[1:3] // 切片 p = 0xc00000c060,arr = [1 2],len = 2,cap = 4 fmt.Printf("切片 p = %p,arr = %+v,len = %d,cap = %d\n", &arrSlice, arrSlice, len(arrSlice), cap(arrSlice))
咱们上面有提到过,切片是对数组的一个引用,那么咱们修改数组的值,切片会发生什么呢?文档
// 把数组 index = 1 的值修改成 10 arr[1] = 10 // 切片 p = 0xc00000c060,arr = [10 2],len = 2,cap = 4 fmt.Printf("切片 p = %p,arr = %+v,len = %d,cap = %d\n", &arrSlice, arrSlice, len(arrSlice), cap(arrSlice)
能够看到,咱们切片对应的值也被修改成 10,切片的底层数据结构中存在的底层数组是对咱们上面数组作的引用传递
,关于引用传递和值传递你们应该仍是分的清的import
那么对应的,咱们修改切片,数组也会发生相应的变化基础
// 一样的道理,修改切片的值也会影响到底层数组 arrSlice[0] = 8 // 切片 p = 0xc00000c060,arr = [8 2],len = 2,cap = 4 fmt.Printf("切片 p = %p,arr = %+v,len = %d,cap = %d\n", &arrSlice, arrSlice, len(arrSlice), cap(arrSlice)) // 数组 p = 0xc00001c0f0,arr = [0 8 2 3 4],len = 5,cap = 5 fmt.Printf("数组 p = %p,arr = %+v,len = %d,cap = %d\n", &arr, arr, len(arr), cap(arr))
上面说到,咱们修改切片或者是数组,都会有对应的变化,那么咱们对切片进行追加呢?引用
// 对切片追加 arrSlice = append(arrSlice, 11) // 切片 p = 0xc00008a020,arr = [8 2 11],len = 3,cap = 4 fmt.Printf("切片 p = %p,arr = %+v,len = %d,cap = %d\n", &arrSlice, arrSlice, len(arrSlice), cap(arrSlice)) // 数组 p = 0xc000090060,arr = [0 8 2 11 4],len = 5,cap = 5 fmt.Printf("数组 p = %p,arr = %+v,len = %d,cap = %d\n", &arr, arr, len(arr), cap(arr))
咱们能够看到,数组的 index = 3 的值被修改成 11,我是这么理解的:
[0,1]
对应着数组的 [1,2]
[0,1,2]
数组对应的索引是 [1,2,3]
经过上面的代码,咱们能够看到咱们的 cap 一直都是 4,为何呢?这就要涉及到一个容量的计算公式了
建立时计算容量的公式为:建立时的长度 * 2,咱们的长度是 2 ,计算出来的容量则为 4
在追加时,会先判断容量够不够,若是容量足够则容量不变;若是超出容量那么判断长度是否小于 1024 ,小于则容量 * 2,大于则容量 * 1.25
这就是为何咱们的容量一直是 4 的缘由
咱们一直在反复提,切片是对数组的引用,那么当我切片已经超出数组的范围,会发生什么呢?
咱们写代码操做一下
arrSlice = append(arrSlice, 12, 13, 14) // 切片 p = 0xc00000c060,arr = [8 2 11 12 13 14],len = 6,cap = 8 fmt.Printf("切片 p = %p,arr = %+v,len = %d,cap = %d\n", &arrSlice, arrSlice, len(arrSlice), cap(arrSlice)) // 数组 p = 0xc00001c0f0,arr = [0 8 2 11 4],len = 5,cap = 5 fmt.Printf("数组 p = %p,arr = %+v,len = %d,cap = %d\n", &arr, arr, len(arr), cap(arr))
在上面代码中,咱们追加了三个值 12,13,14
。
咱们能够看到,数组的值并无被修改,按照咱们上一步讲的来讲,应该数组的最后一位会变为 12
,可是并无
这是一个很重要的概念,当咱们的切片容量大于底层数组容量时,会自动建立一个新的底层数组,取消对原数组的引用
当咱们切片取消对原数组引用时,咱们再去修改切片,并不会影响到原数组
arrSlice[0] = 18 // 切片 p = 0xc00008c020,arr = [18 2 11 12 13 14],len = 6,cap = 8 fmt.Printf("切片 p = %p,arr = %+v,len = %d,cap = %d\n", &arrSlice, arrSlice, len(arrSlice), cap(arrSlice)) // 数组 p = 0xc000092060,arr = [0 8 2 11 4],len = 5,cap = 5 fmt.Printf("数组 p = %p,arr = %+v,len = %d,cap = %d\n", &arr, arr, len(arr), cap(arr))
根据查阅资料来看,大部分人都是说当被取消引用,而且也没有别的地方去引用时,golang 的垃圾处理会自动回收;你们能够具体尝试一下
此文章完整代码以下
// Enda <endachao@gmail.com> package main import "fmt" func main() { //数组是固定长度与容量,而且具备相同类型的一组值 //此定义的数组长度为 5 ,那么容量也会固定为 5 arr := [5]int{0, 1, 2, 3, 4} // 数组 p = 0xc00001c0f0,arr = [0 1 2 3 4],len = 5,cap = 5 fmt.Printf("数组 p = %p,arr = %+v,len = %d,cap = %d\n", &arr, arr, len(arr), cap(arr)) // 数组的索引都是从 0 开始的 // 0 1 fmt.Println(arr[0], arr[1]) // 切片能够看作是一个可变长的数组 // 切片能够看作是对数组的一个片断的引用 // [1:3] 表示范围从数组的索引 1 至 数组的索引 3,它是不包括 3 的,能够简单理解为 index == 1 && index < 3 // 当切片生成后,索引默认也是从 0 开始, 切片索引是 [0,1] 对应着数组的索引是 [1,2] arrSlice := arr[1:3] // 切片 p = 0xc00000c060,arr = [1 2],len = 2,cap = 4 fmt.Printf("切片 p = %p,arr = %+v,len = %d,cap = %d\n", &arrSlice, arrSlice, len(arrSlice), cap(arrSlice)) // 因为切片是对底层数据的一个引用,因此修改底层数组会更改切片的值 arr[1] = 10 // 切片 p = 0xc00000c060,arr = [10 2],len = 2,cap = 4 fmt.Printf("切片 p = %p,arr = %+v,len = %d,cap = %d\n", &arrSlice, arrSlice, len(arrSlice), cap(arrSlice)) // 一样的道理,修改切片的值也会影响到底层数组 arrSlice[0] = 8 // 切片 p = 0xc00000c060,arr = [8 2],len = 2,cap = 4 fmt.Printf("切片 p = %p,arr = %+v,len = %d,cap = %d\n", &arrSlice, arrSlice, len(arrSlice), cap(arrSlice)) // 数组 p = 0xc00001c0f0,arr = [0 8 2 3 4],len = 5,cap = 5 fmt.Printf("数组 p = %p,arr = %+v,len = %d,cap = %d\n", &arr, arr, len(arr), cap(arr)) // 对切片追加 // 追加前的切片索引是 [0,1] 对应着 数组的 [1,2] // 在这里追加了一位,那么切片的索引是 [0,1,2] 数组对应的索引是 [1,2,3] // 因此追加的值,也会修改数组的值 arrSlice = append(arrSlice, 11) // 切片 p = 0xc00008a020,arr = [8 2 11],len = 3,cap = 4 fmt.Printf("切片 p = %p,arr = %+v,len = %d,cap = %d\n", &arrSlice, arrSlice, len(arrSlice), cap(arrSlice)) // 数组 p = 0xc000090060,arr = [0 8 2 11 4],len = 5,cap = 5 fmt.Printf("数组 p = %p,arr = %+v,len = %d,cap = %d\n", &arr, arr, len(arr), cap(arr)) // 切片的容量为何是 4 // 在建立切片时,咱们指定了对数组的 [1,3] 进行切片,这里切出来的长度为 2 // 切片在建立时,若是不指定容量,那么容量会自动去计算 // 建立时计算容量的公式为:建立时的长度 * 2 // 追加时,会先判断长度够不够,若是够则容量不变,若是不够那么判断长度是否小于 1024 ,小于则容量 * 2,大于则 容量 * 1.25 // 当咱们切片的长度超过了原数组的长度 // 咱们能够看到,数组的值并无被修改,按照咱们上一步讲的来讲,应该数组的最后一位会变为 12,可是并无 // 这是一个很重要的概念,当咱们的切片容量大于底层数组容量时,会自动建立一个新的底层数组 arrSlice = append(arrSlice, 12, 13, 14) // 切片 p = 0xc00000c060,arr = [8 2 11 12 13 14],len = 6,cap = 8 fmt.Printf("切片 p = %p,arr = %+v,len = %d,cap = %d\n", &arrSlice, arrSlice, len(arrSlice), cap(arrSlice)) // 数组 p = 0xc00001c0f0,arr = [0 8 2 11 4],len = 5,cap = 5 fmt.Printf("数组 p = %p,arr = %+v,len = %d,cap = %d\n", &arr, arr, len(arr), cap(arr)) //当我再去修改切片的值时,并不会去操做咱们上面定义的数组了 arrSlice[0] = 18 // 切片 p = 0xc00008c020,arr = [18 2 11 12 13 14],len = 6,cap = 8 fmt.Printf("切片 p = %p,arr = %+v,len = %d,cap = %d\n", &arrSlice, arrSlice, len(arrSlice), cap(arrSlice)) // 数组 p = 0xc000092060,arr = [0 8 2 11 4],len = 5,cap = 5 fmt.Printf("数组 p = %p,arr = %+v,len = %d,cap = %d\n", &arr, arr, len(arr), cap(arr)) }