共计 5797 个字符,预计需要花费 15 分钟才能阅读完成。
前言
数组的长度是申明的时候就固定好的,前面不可能变大,而且长度和容量相等。
切片的长度和容量前面能够随着元素增多而增长,然而容量不可能小于长度。
注释
申明 & 初始化
在 Go 中申明即初始化,如果在申明的时候没有初始化值,那么就会赋值为申明类型的「零值」。
func TestDemo1(t *testing.T) {
// 数组
var array1 [5]int // 只需设置长度,前面不可变
var array2 = new([5]int) // 返回指针
// 切片
var slice1 []int
var slice2 = make([]int, 5, 5) // 设置长度、容量,前面可变
t.Log("array1 val:", array1) // [0 0 0 0 0]
t.Log("array1 len:", len(array1)) // 5
t.Log("array1 cap:", cap(array1)) // 5
fmt.Println("")
t.Log("array2 val:", array2) // &[0 0 0 0 0]
t.Log("array2 len:", len(array2)) // 5
t.Log("array2 cap:", cap(array2)) // 5
fmt.Println("")
t.Log("slice1 val:", slice1) // []
t.Log("slice1 len:", len(slice1)) // 0
t.Log("slice1 cap:", cap(slice1)) // 0
fmt.Println("")
t.Log("slice2 val:", slice2) // [0 0 0 0 0]
t.Log("slice2 len:", len(slice2)) // 5
t.Log("slice2 cap:", cap(slice2)) // 5
}
在申明的时候就初始化:
func TestDemo2(t *testing.T) {
// 数组
var array1 = [5]int{4: 1, 2: 5}
var array2 = [...]int{4: 1, 2: 5}
// 切片
var slice1 = []int{4: 1, 2: 5}
var slice2 = array1[:] // 从数组截取来的切片
t.Log("array1 val:", array1) // [0 0 5 0 1]
t.Log("array1 len:", len(array1)) // 5
t.Log("array1 cap:", cap(array1)) // 5
fmt.Println("")
t.Log("array2 val:", array2) // [0 0 5 0 1]
t.Log("array2 len:", len(array2)) // 5
t.Log("array2 cap:", cap(array2)) // 5
fmt.Println("")
t.Log("slice1 val:", slice1) // [0 0 5 0 1]
t.Log("slice1 len:", len(slice1)) // 5
t.Log("slice1 cap:", cap(slice1)) // 5
fmt.Println("")
t.Log("slice2 val:", slice2) // [0 0 5 0 1]
t.Log("slice2 len:", len(slice2)) // 5
t.Log("slice2 cap:", cap(slice2)) // 5
}
增加 & 更新元素值
数组因为长度固定,且的值都是初始化好了的,所以只有更新。
切片更新操作和数据一样,只不过新增元素只能通过 append() 办法。
append():将元素追加大切片的开端,如果容量不够,会进行扩容。
func TestDemo3(t *testing.T) {
// 数组
var array1 = [5]int{4: 1, 2: 5}
array1[0] = 100 // 更新
array1[4] = 100 // 更新
// 切片
var slice1 = []int{4: 1, 2: 5}
array1[4] = 100 // 更新
//array1[5] = 100 // 报错
slice1 = append(slice1, 1) // 切片减少元素只能应用此办法
t.Log("array1 val:", array1) // [100 0 5 0 100]
t.Log("array1 len:", len(array1)) // 5
t.Log("array1 cap:", cap(array1)) // 5
fmt.Println("")
t.Log("slice1 val:", slice1) // [0 0 5 0 1 1]
t.Log("slice1 len:", len(slice1)) // 6
t.Log("slice1 cap:", cap(slice1)) // 10
}
表达式
数组与切片,都能够应用表达式截取,截取之后的数据它的类型为切片。
func TestDemo4(t *testing.T) {array1 := [10]int{9, 1, 7, 3, 0, 5, 6, 2, 8, 4}
slice1 := array1[3:] // 从 index 3 取到 index end
t.Log("slice1 val:", slice1) // [3 0 5 6 2 8 4]
t.Log("slice1 len:", len(slice1)) // 7
t.Log("slice1 cap:", cap(slice1)) // 7
fmt.Println("")
slice2 := array1[3:4] // 从 index 3 取到 index 4
t.Log("slice2 val:", slice2) // [3]
t.Log("slice2 len:", len(slice2)) // 1
t.Log("slice2 cap:", cap(slice2)) // 7
fmt.Println("")
slice3 := array1[3:6:6] // 从 index 3 取到 index 6,容量取到 index 6
t.Log("slice3 val:", slice3) // [3 0 5]
t.Log("slice3 len:", len(slice3)) // 3
t.Log("slice3 cap:", cap(slice3)) // 3
fmt.Println("")
slice4 := array1[3:6:9] // 从 index 3 取到 index 6,容量取到 index 9
t.Log("slice4 val:", slice4) // [3 0 5]
t.Log("slice4 len:", len(slice4)) // 3
t.Log("slice4 cap:", cap(slice4)) // 6
}
遍历
应用 for、range
func TestDemo5(t *testing.T) {array1 := [...]int{9, 1, 7, 3, 0, 5, 6, 2, 8, 4}
slice1 := make([]int, 5, 5)
for k, v := range array1 {fmt.Println(k, "-", v)
}
fmt.Println()
for k, v := range slice1 {fmt.Println(k, "-", v)
}
}
比拟
数组与数组能够应用 == 比拟,不能与 nil 比拟
切片与切片不能应用 == 比拟,能够应用 reflect.DeepEqual 比拟,能够与 nil 比拟
func TestDemo6(t *testing.T) {array1 := [...]int{9, 1, 7, 3, 0, 5, 6, 2, 8, 4}
array2 := [...]int{9, 1, 7, 3, 0, 5, 6, 2, 8, 9}
array3 := [...]int{9, 1, 7, 3, 0, 5, 6, 2, 8, 9}
t.Logf("array1 == array2 %t\n", array1 == array2) // false
t.Logf("array2 == array3 %t\n", array2 == array3) // true
//t.Logf("%t\n", array2 == nil) // 会报错,数组不能与 nil 比
slice1 := make([]int, 5, 5)
var slice2 []int
slice3 := []int{4: 0}
// t.Logf("%t\n", slice1 == slice2) // 会报错,切片与切片不能比
t.Logf("slice1 == nil %t\n", slice1 == nil) // false
t.Logf("slice2 == nil %t\n", slice2 == nil) // true
t.Logf("slice3 == nil %t\n", slice3 == nil) // false
t.Logf("slice1 == slice2 %t\n", reflect.DeepEqual(slice1, slice2)) // false
t.Logf("slice2 == slice3 %t\n", reflect.DeepEqual(slice2, slice3)) // false
t.Logf("slice1 == slice3 %t\n", reflect.DeepEqual(slice1, slice3)) // true
}
删除
需应用 append()、切片表达式 联合来实现
func TestDemo7(t *testing.T) {slice1 := []int{9, 1, 7, 3, 0, 5, 6, 2, 8, 4}
slice1 = append(slice1[:2], slice1[3:]...)
t.Log(slice1)
}
扩大
数组与切片的关系
数组为值类型,切片为援用类型,他们又有何关系呢?
程序示例:
func TestDemo8(t *testing.T) {array1 := [10]int{9, 1, 7, 3, 0, 5, 6, 2, 8, 4}
slice1 := array1[:]
t.Log("slice1 val:", slice1) // [9 1 7 3 0 5 6 2 8 4]
t.Log("slice1 len:", len(slice1)) // 10
t.Log("slice1 cap:", cap(slice1)) // 10
array1[9] = 96969696 // array1 的批改会影响到 slice1
fmt.Println("")
t.Log("slice1 val:", slice1) // [9 1 7 3 0 5 6 2 8 96969696]
t.Log("slice1 len:", len(slice1)) // 10
t.Log("slice1 cap:", cap(slice1)) // 10
}
在这个示例程序中,能够说 slice1 是 array1 的援用。
不光是在示例程序中,这种在数组上通过表达式截取出的切片,为数组的援用,就算在程序中,间接申明一个新切片(var slice1 []int),在切片的底层实现,其实也是援用了一个数组。
他们的关系就是:数组是切片的底层实现,切片是数组的援用。
切片扩容
在示例程序 TestDemo8 中,slice1 会始终援用 array1 么?
个别状况下是这样,但有种状况下援用会发生变化,就是在 slice 产生扩容的状况下
func TestDemo9(t *testing.T) {array1 := [10]int{9, 1, 7, 3, 0, 5, 6, 2, 8, 4}
slice1 := array1[:] // 从 array1 截取出 slice1
t.Log("slice1 val:", slice1) // [9 1 7 3 0 5 6 2 8 4]
t.Log("slice1 len:", len(slice1)) // 10
t.Log("slice1 cap:", cap(slice1)) // 10
slice1 = append(slice1, 9) // 进行扩容后,slice1 指向了新的底层数组,不在是 array1 的援用
array1[9] = 96969696
fmt.Println("")
t.Log("slice1 val:", slice1) // [9 1 7 3 0 5 6 2 8 4 9]
t.Log("slice1 len:", len(slice1)) // 11
t.Log("slice1 cap:", cap(slice1)) // 20
}
当切片增加新元素,发现容量不够时,会开拓一个新的底层数组,而后把旧数组的数据和增加的新元素一并拷贝到新数组中。
扩容策略:
- 首先判断,如果新申请容量(cap)大于 2 倍的旧容量(old.cap),最终容量(newcap)就是新申请的容量(cap)
- 否则判断,如果旧切片的长度小于 1024,则最终容量 (newcap) 就是旧容量 (old.cap) 的两倍,即(newcap=doublecap)
- 否则判断,如果旧切片长度大于等于 1024,则最终容量(newcap)从旧容量(old.cap)开始循环减少原来的 1/4,即(newcap=old.cap,for {newcap += newcap/4})直到最终容量(newcap)大于等于新申请的容量(cap),即(newcap >= cap)
- 如果最终容量(cap)计算值溢出,则最终容量(cap)就是新申请容量(cap)
深复制
靠扩容解决援用问题,显得不是那么优雅。
能够应用 copy() 进行深复制
func TestDemo10(t *testing.T) {array1 := [10]int{9, 1, 7, 3, 0, 5, 6, 2, 8, 4}
var slice1 = make([]int, 10, 10)
copy(slice1, array1[:]) // 深复制,slice1 不会援用 array1
array1[9] = 96969696
t.Log("slice1 val:", slice1) // [9 1 7 3 0 5 6 2 8 4]
t.Log("slice1 len:", len(slice1)) // 10
t.Log("slice1 cap:", cap(slice1)) // 10
fmt.Println("")
t.Log("array1 val:", array1) // [9 1 7 3 0 5 6 2 8 96969696]
t.Log("array1 len:", len(array1)) // 10
t.Log("array1 cap:", cap(array1)) // 10
}
数组
一段间断的内存空间。
make
make 只能用于 slice、map、channel,返回的初始化后的(非零)值。
援用类型
- 切片
- 字典
- 通道
- 函数
值类型
- 数组
- 根底数据类型
- 构造体类型
总结
- 切片是数组的援用,数组是切片的底层实现。
- 数组的长度 (len) 等于容量 (cap),切片的长度(len) 小于等于容量(cap)。
- 数组申明的时候默认就会初始化,值为类型的「零值」;切片申明的时候不会默认初始化,值是 nil。
- 应用 copy() 深复制解决援用问题。
文章示例代码
Sown 专栏地址:https://segmentfault.com/blog/sown