背景
Go语言开发过程中遇到需要拷贝一个slice的操作,go提供 builtin 库提供了一个copy方法
恰好在写代码时看到一段copy使用的代码 会输出怎样的结果?
src := []string{"ip", "tcp", "http"}
dst := make([]string, 0, len(src))
copy(dst, src)
fmt.Printf("src=%v \n dst %v\n", src, dst)
分析原因
- copy函数拷贝的数量是 len(src) 和 len(dst) 中的最小值。
- 拷贝操作从src中取最小个元素从头开始覆盖dst中的元素,dst剩余的元素保留
- 特别:入参支持string 拷贝到 bytes 类型的数组
深浅拷贝
本质是浅拷贝,验证得出结论
- slice元素为指针类型的,浅拷贝
- slice元素为非指针类型的,深拷贝
验证代码
package main
import (
"fmt"
"github.com/aws/aws-sdk-go-v2/aws"
)
type Parent struct {
Num *int `json:"num"`
Name *string `json:"name"`
}
func main() {
src := []string{"a", "b", "c"}
dst := make([]string, 0, len(src))
copy(dst, src)
fmt.Printf("src=%v \n dst %v\n", src, dst)
//拷贝的使用
TestSliceCopy()
//元素是指针类型: 浅拷贝
TestSliceCopyTypePointElement()
//元素非指针类型: 元素改变时
TestSliceCopyTypeNoPointElement()
}
func TestSliceCopy() {
size := 3
//初始化并打印
src := initSlice(size, "src")
printSlice(src, "src")
//执行拷贝,初始化元素数量相等,这是一般的拷贝使用方法
dst1 := make([]*Parent, len(src))
copy(dst1, src)
printSlice(dst1, "dst1")
//误区:初始化元素数量为0,拷贝不到任何元素, 原因是:【copy函数数量是 len(src) 和 len(dst) 中的最小值】
dst2 := make([]*Parent, 0, len(src))
copy(dst2, src)
//slice3没有拷贝到任何元素:原因:由于len(dst2)==0,所以不会被拷贝
printSlice(dst2, "dst2-0个元素")
dst2 = make([]*Parent, 1, len(src))
copy(dst2, src)
printSlice(dst2, "dst2-1个元素")
//【copy函数数量是 len(src) 和 len(dst) 中的最小值】
dst3 := make([]*Parent, size+1)
copy(dst3, src)
//dst3,第4个为nil 原因是,被拷贝的
printSlice(dst3, "dst3")
// 初始化5个元素,再拷贝从有3个元素的slice1过来,结果:前3个数据被覆盖
dst5 := initSlice(5, "dst5")
printSlice(dst5, "dst5-初始化")
copy(dst5, src)
printSlice(dst5, "dst5-拷贝后")
/**
* 结论1:copy函数拷贝的数量是 len(src) 和 len(dst) 中的最小值copyNum,
* 拷贝操作从src中取copyNum个元素从头开始覆盖dst中的元素,dst剩余的元素保留
*/
}
func printSlice(slice []*Parent, name string) {
println(fmt.Sprintf("------%s------len=%d", name, len(slice)))
for i := range slice {
if slice[i] == nil {
println(fmt.Sprintf("index-%d addr=%p is nil", i, slice[i]))
continue
}
println(fmt.Sprintf("index-%d: num=%v,name=%v,addr=%p,parent=%#v", i, aws.ToInt(slice[i].Num), aws.ToString(slice[i].Name), slice[i], slice[i]))
}
println(fmt.Sprintf("------%s------", name))
}
func printNotPointSlice(slice []Parent, name string) {
println(fmt.Sprintf("------%s------len=%d", name, len(slice)))
for i := range slice {
println(fmt.Sprintf("index-%d: num=%v,name=%v,addr=%p,parent=%#v", i, aws.ToInt(slice[i].Num), aws.ToString(slice[i].Name), &slice[i], slice[i]))
}
println(fmt.Sprintf("------%s------", name))
}
func TestSliceCopyTypePointElement() {
size := 3
src := initSlice(size, "src")
printSlice(src, "src")
// 拷贝到slice2
dst := make([]*Parent, len(src))
printSlice(dst, "dst-拷贝前")
copy(dst, src)
printSlice(dst, "dst-拷贝后")
// 拷贝后地址
// 更新slice2的name
for i := range dst {
newName := aws.ToString(dst[i].Name) + "_Updated"
dst[i].Name = &newName
}
printSlice(dst, "dst-更新名称")
printSlice(src, "src-名称变化")
}
func TestSliceCopyTypeNoPointElement() {
size := 3
src := initNotPointSlice(size, "src")
printNotPointSlice(src, "src")
// 拷贝到slice2
dst := make([]Parent, len(src))
printNotPointSlice(dst, "dst-拷贝前")
copy(dst, src)
printNotPointSlice(dst, "dst-拷贝后")
// 更新slice2的name
for i := range dst {
newName := aws.ToString(dst[i].Name) + "_Updated"
dst[i].Name = &newName
}
printNotPointSlice(dst, "dst-更新名称")
printNotPointSlice(src, "src-名称变化")
}
func initSlice(size int, namePrefix string) []*Parent {
slice := make([]*Parent, 0, size)
for idx := 0; idx < size; idx++ {
var num = idx + 1
slice = append(slice, &Parent{
Name: aws.String(fmt.Sprintf("%s_%d", namePrefix, num)),
Num: &num,
})
}
return slice
}
func initNotPointSlice(size int, namePrefix string) []Parent {
slice := make([]Parent, 0, size)
for idx := 0; idx < size; idx++ {
var num = idx + 1
slice = append(slice, Parent{
Name: aws.String(fmt.Sprintf("%s_%d", namePrefix, num)),
Num: &num,
})
}
return slice
}