Generics 复制切片的通用方法
我需要复制片(以及底层数组的一部分),这样调用者就不会改变数组的原始元素。我想我可以为特定类型的数组编写一个函数:Generics 复制切片的通用方法,generics,go,Generics,Go,我需要复制片(以及底层数组的一部分),这样调用者就不会改变数组的原始元素。我想我可以为特定类型的数组编写一个函数: func duplicateSliceOfSomeType(sliceOfSomeType []SomeType) []SomeType { dulicate := make([]SomeType, len(sliceOfSomeType)) copy(duplicate, sliceOfSomeType) return duplicate } 但是有没有
func duplicateSliceOfSomeType(sliceOfSomeType []SomeType) []SomeType {
dulicate := make([]SomeType, len(sliceOfSomeType))
copy(duplicate, sliceOfSomeType)
return duplicate
}
但是有没有一种方法可以泛型地创建相同的方法,也许没有泛型
func duplicateSlice(slice []?) []?{
duplicate := make([]?, len(slice))
copy(duplicate, slice)
return duplicate
}
你可以写一个简单的语句来制作一个切片的浅拷贝
b := append([]T(nil), a...)
相当于,
b := make([]T, len(a))
copy(b, a)
比如说,
package main
import "fmt"
type T int
func main() {
a := []T{4, 2}
b := append([]T(nil), a...)
fmt.Println(&a[0], a, &b[0], b)
b[0] = 9
fmt.Println(&a[0], a, &b[0], b)
}
输出:
0x10328000 [4 2] 0x10328020 [4 2]
0x10328000 [4 2] 0x10328020 [9 2]
0xc20800a200 [4 2] 0xc20800a210 [4 2]
0xc20800a200 [4 2] 0xc20800a210 [9 2]
0xc20800a290 [4 2] 0xc20800a2a0 [4 2]
0xc20800a290 [4 2] 0xc20800a2a0 [9 2]
0xc20800a310 [4 2] 0xc20800a320 [4 2]
0xc20800a310 [4 2] 0xc20800a320 [9 2]
增编:
如果人们是新手,他们根本不应该使用反射
-抢劫
即使对专家来说,反思也是微妙的。它暴露了谁的细节
理解取决于对如何理解的基本知识的了解
该语言的工作原理以及(在较小程度上)它的实现方式。信息技术
即使是有经验的围棋程序员也会感到困惑;新
铸造的地鼠有更重要、更简单的东西要学
第一。那些过早学习反思的人把自己搞糊涂了
他们对这些基本原理的理解。最好保持在手臂的位置
直到图片的其余部分清晰为止
-抢劫
也就是说
package main
import (
"fmt"
"reflect"
)
func CopySlice(s interface{}) interface{} {
t, v := reflect.TypeOf(s), reflect.ValueOf(s)
c := reflect.MakeSlice(t, v.Len(), v.Len())
reflect.Copy(c, v)
return c.Interface()
}
type T int
func main() {
{
// append
a := []T{4, 2}
b := append([]T(nil), a...)
fmt.Println(&a[0], a, &b[0], b)
b[0] = 9
fmt.Println(&a[0], a, &b[0], b)
}
{
// make and copy
a := []T{4, 2}
b := make([]T, len(a))
copy(b, a)
fmt.Println(&a[0], a, &b[0], b)
b[0] = 9
fmt.Println(&a[0], a, &b[0], b)
}
{
// reflection
a := []T{4, 2}
b := CopySlice(a).([]T)
fmt.Println(&a[0], a, &b[0], b)
b[0] = 9
fmt.Println(&a[0], a, &b[0], b)
}
}
输出:
0x10328000 [4 2] 0x10328020 [4 2]
0x10328000 [4 2] 0x10328020 [9 2]
0xc20800a200 [4 2] 0xc20800a210 [4 2]
0xc20800a200 [4 2] 0xc20800a210 [9 2]
0xc20800a290 [4 2] 0xc20800a2a0 [4 2]
0xc20800a290 [4 2] 0xc20800a2a0 [9 2]
0xc20800a310 [4 2] 0xc20800a320 [4 2]
0xc20800a310 [4 2] 0xc20800a320 [9 2]
您可以使用
reflect
包对任何类型进行复制,特别是reflect.copy
:在Go 1.18(2022年初发布的候选版本)中引入类型参数,这将是微不足道的
基于,您可以编写如下通用函数:
func duplicateSlice[T any](src []T) []T {
dup := make([]T, len(src))
copy(dup, src)
return dup
}
并按如下方式使用:
package main
import (
"fmt"
)
func duplicateSlice[T any](src []T) []T {
dup := make([]T, len(src))
copy(dup, src)
return dup
}
func main() {
a := []string{"foo", "bar"}
a2 := duplicateSlice(a)
a[0] = "baz"
fmt.Println(a) // [baz bar]
fmt.Println(a2) // [foo bar]
b := []uint64{8, 12, 30}
b2 := duplicateSlice(b)
b[0] = 7
fmt.Println(b) // [7 12 30]
fmt.Println(b2) // [8 12 30]
}
您可以在此中运行此代码。为了完整性,要进一步展开,下面是一个如何使用
反射的示例。在Go 1.18之前,复制以完成相同的操作:
package main
import (
"fmt"
"reflect"
)
func main() {
src := []int{1,2,3}
target := duplicateSlice(src)
src[0] = 9
fmt.Println(src) // [9 2 3]
fmt.Println(target) // [1 2 3]
}
func duplicateSlice(src interface{}) interface{} {
t := reflect.TypeOf(src)
if t.Kind() != reflect.Slice {
panic("not a slice!")
}
v := reflect.ValueOf(src)
target := reflect.MakeSlice(t, v.Cap(), v.Len())
reflect.Copy(target, v)
return target
}
请记住,使用reflect
包要比当前应用程序中的方法慢得多。考虑这里给出的代码只是一个设计的例子供参考。
链接到游乐场:所以本质上你是在为分配给b
的新数组将a
的元素添加到一个新的空片段中,对吗?append
方法的性能是否与make
/copy
方法一样?@Kaiged:append-to-anil
切片转换为make
和copy
。看我修改过的答案。@Kaiged:它们是一样的。对于a:=make([]T,256)
,BenchmarkAppend 7026 ns/op.BenchmarkMakeCopy 7054 ns/op。