欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Golang slice

程序员文章站 2024-03-01 13:24:58
...

创建切片,len、cap、append

b := make([]int, 5)
println(len(b), cap(b)) // 输出结果是:5, 5

fmt.Println(b) // 输出结果是:[0 0 0 0 0]

上述代码是生成默认占用5个0值的切片,下面的输出结果是另一回事

b := make([]int, 0, 5)
println(len(b), cap(b)) // 输出结果是:0, 5

fmt.Println(b) // 输出结果是:[]

上述代码是生成cap长度为5,实际使用长度为0的切片,在指定的cap内进行append操作,是不会发生内存拷贝扩容操作。

b := make([]int, 0, 2)
fmt.Printf("%d, %d, %p\n", len(b), cap(b), b) // 0, 2, 0xc420012190

b = append(b, 1)
b = append(b, 2)
fmt.Printf("%d, %d, %p\n", len(b), cap(b), b) // 2, 2, 0xc420012190

b = append(b, 3)
fmt.Printf("%d, %d, %p\n", len(b), cap(b), b) // 3, 4, 0xc420012198

最后一行输出显示,内存地址跟前2次输出的不一样,并且cap值也在原来的基础上,翻了一倍,相当于做了如下操作:

// mock append
tmp := make([]int, 0, cap(b) * 2) // 是当前的cap值的翻倍
// 复制操作略过。。。
b = tmp

copy

正常的切片copy操作

a := make([]int, 5)
a[0] = 1
a[1] = 2
fmt.Println(a) // [1 2 0 0 0]

b := make([]int, 5)
b[0] = 11
b[1] = 22
b[2] = 33
b[3] = 44
b[4] = 55
fmt.Println(b) // [11 22 33 44 55]

copy(b, a)
fmt.Println(b) // [1 2 0 0 0]

另一个效果

a := make([]int, 5)
a[0] = 1
a[1] = 2
fmt.Println(a) // [1 2 0 0 0]

b := make([]int, 0, 5) // len(b)!=cap(b)
b = append(b, 11)
b = append(b, 22)
fmt.Println(b) // [11, 22]

copy(b, a)
fmt.Println(b) // [1 2]

第三行的输出,只是[1 2],不是[1 2 0 0 0],是对b切片里len(b)长度内的元素进行对应下标复制操作,假如len(b)==0,那输出结果是[]。 这是需要小心的地方,老司机会一不留神搞错,不了解的人那就更难说。

切片的切片

a := make([]int, 5)
a[0] = 1
a[1] = 2
a[2] = 3
a[3] = 4
a[4] = 5
// 原始数据输出
fmt.Printf("%v, %p\n", a, a) // [1 2 3 4 5], 0xc4200141b0

aslice1 := a[1:]
// 第一组输出
fmt.Printf("%v, %p\n", aslice1, aslice1) // [2 3 4 5], 0xc4200141b8
fmt.Println(len(aslice1), cap(aslice1)) // 4 4

aslice2 := a[1:3]
// 第二组输出
fmt.Printf("%v, %p\n", aslice2, aslice2) // [2 3], 0xc4200141b8
fmt.Println(len(aslice2), cap(aslice2)) // 2 4

aslice3 := a[:3]
// 第三组输出
fmt.Printf("%v, %p\n", aslice3, aslice3) // [1 2 3], 0xc4200141b0
fmt.Println(len(aslice3), cap(aslice3)) // 3 5

上面几组输出,粗看没什么,细看还是有值得注意的

  1. 每组的len和cap值都不太一样
  2. 切片的内存地址不是完全相同

len的值好理解,无异议。

cap的值:
  1. 第一组输出 中是4,是新切片首地址到原始切片尾地址的个数。
  2. 第二组输出 中也是4,是新切片首地址到原始切片尾地址的个数
  3. 第三组输出 中是5,道理如上
内存地址:
  1. 原始数据输出的地址和第三组输出的地址一样
  2. 第一组输出的地址和第二组输出的地址一样

这样输出的原因是,指向的切片首地址一样。在64位操作系统,int类型占8个字节,第二组输出的地址比第三组输出的地址多8个数值。若有新的切片是如下定义:

aslice4 := a[2:]
fmt.Println("%p\n", aslice4) // 0xc4200141c0

那输出结果是:0xc4200141c0,是 0xc4200141b8+8 的结果

[]interface{}与[]T

a := []interface{}{1, 2, 3}
a1 := make([]int, 3)

copy(a1, a) // 编译出错
a1 = a // 编译出错

// 要将a里各项值都传给a1的正确做法
for index := range a {
    a1[index] = a[index].(int)
}