|
|
我们上节末尾将要讲向切片里添加元素。
使用append([SliceName], element1, element2,...)的方式可以制作一个名叫SliceName的切片添加完element1, element2, ...之后的切片。
示例:
- package main
- import "fmt"
- func main() {
- var s []int
- printSlice(s)
- // append works on nil slices.
- s = append(s, 0)
- printSlice(s)
- // The slice grows as needed.
- s = append(s, 1)
- printSlice(s)
- // We can add more than one element at a time.
- s = append(s, 2, 3, 4)
- printSlice(s)
- }
- func printSlice(s []int) {
- fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
- }
复制代码 输出:
- len=0 cap=0 []
- len=1 cap=1 [0]
- len=2 cap=2 [0 1]
- len=5 cap=6 [0 1 2 3 4]
复制代码 当然这是使用内置方案操作切片的方式,我们接下来讲一种更为原理性的方式(稍微复杂一点,如果不是需要深入了解Go的同志们可以直接跳到文章末尾)。
想要扩展切片很显然可以使用make函数做一个新的切片,它的cap比原来的切片大,然后将原来的切片的所有值全都复制到新做的切片里(这个新切片的位置还有空余!),然后再将新切片的值赋值给原来的切片。
- t := make([]byte, len(s), (cap(s)+1)*2) // +1 in case cap(s) == 0
- for i := range s {
- t[i] = s[i]
- }
- s = t
复制代码
当然我们没有必要使用循环,因为Go有内置的copy方法,能将数组里面的值一一对应复制:
Go语言内置的copy函数用于将源切片中的元素复制到目标切片。它的基本形式是 copy(dst, src),其中dst(目标)和src(源头)都是切片类型。返回值是实际复制的元素个数,这个数值等于dst和src长度中较小的那个。
使用时需要注意几点:第一,copy不会自动扩容目标切片,所以如果目标切片长度小于源切片,只会复制目标长度那么多元素,多余部分被丢弃;如果目标长度大于源,则只复制源的所有元素,目标剩余位置保持不变。第二,copy支持源和目标为同一切片的不同重叠区域,复制行为是安全的,会按预期处理重叠部分。第三,copy处理的是元素值复制,对于指针或引用类型,复制的是指针或引用本身,不是深层拷贝。第四,copy也可以用于将字符串复制到字节切片,因为字符串可以视为只读的字节源,但目标必须是[]byte。
实际使用中,常见场景包括:合并切片时先make足够大的目标切片然后copy;删除切片中间元素时用copy移动后续元素;或者将数据从临时缓冲区复制到固定大小数组的切片视图等。总之,copy是切片操作中高效且常用的基础函数,理解其长度限制和返回值有助于避免数据丢失或意外截断。
假设 a := []int{1, 2},b := []int{9, 9, 9}。copy(b, a) 会把 [1,2] 复制到 b 的开头,结果是 b = [1, 2, 9]。如果调换成 copy(a, b),因为 a 长度是2,只会把 b 的前两个元素 [9,9] 复制到 a 中,结果是 a = [9, 9],而 b 保持不变。 简化为:
- t := make([]byte, len(s), (cap(s)+1)*2)
- copy(t, s)
- s = t
复制代码
本文介绍得比较浅薄,强烈建议建议读者移步Go Slices: usage and internals - The Go Programming Language来深入理解slices的内涵。
|
|