Go 为什么戈朗';他的切片操作如此复杂
这是我在Golang的第一天,当我尝试它的切片操作时,比如Go 为什么戈朗';他的切片操作如此复杂,go,Go,这是我在Golang的第一天,当我尝试它的切片操作时,比如append(),有一件事让我很困惑: package main import "fmt" func main() { s := []int{2, 3, 5, 7, 11, 13} a:= s[2:4]; a = append(a, 1000, 1001); a[1] = 100; printSlice("a:", a) printSlice("s:", s) } func prin
append()
,有一件事让我很困惑:
package main
import "fmt"
func main() {
s := []int{2, 3, 5, 7, 11, 13}
a:= s[2:4];
a = append(a, 1000, 1001);
a[1] = 100;
printSlice("a:", a)
printSlice("s:", s)
}
func printSlice(title string, s []int) {
fmt.Printf("%s len=%d cap=%d %v\n", title, len(s), cap(s), s)
}
当我只向a
添加两个数字时,如:
a = append(a, 1000, 1001);
…结果是:
a: len=4 cap=4 [5 100 1000 1001]
s: len=6 cap=6 [2 3 5 100 1000 1001]
我认为,它显示了a
作为s
的参考
但当我将代码行更改为:
a = append(a, 1000, 1001, 1002);
…结果是:
a: len=5 cap=8 [5 100 1000 1001 1002]
s: len=6 cap=6 [2 3 5 7 11 13]
我认为,a
已被重新分配到另一段内存,以保存整个内容,并分离对s
的引用
这是如此的不一致,让我非常困惑,以至于有时候很容易犯这个错误(比如当你有一个随机数的值要附加时)
为什么Golang设计成这样?如果我只想在JavaScript的
slice
和push
中执行类似的操作,那么如何避免这种情况呢?这是一个关于如何在Go中实现slice的问题
看起来像:
type slice struct {
array unsafe.Pointer
len int
cap int
}
因此,切片具有长度和容量。如果尝试将项目附加到切片,使其超过当前容量,则会在下面创建一个新数组来保存新数据,但由于以前的子切片可能仍然指向旧的数组,因此它会保持原样,直到不再有对它的引用为止
现在让我们假设我们有一个切片
a
:[1,2,3,4,5,6]
和一个子切片B
,它指向a
:[4,5,6]中的最后3项
[1, 2, 3, 4, 5, 6]
^ ^
| |
| B------
|
A---------------
现在,如果我们在B
中附加一个项,那么根据您的预期行为,它也应该更新A
,因此将因此创建一个新数组。如果子片的大小与实际数组相比很小(用于从原始数组中复制1000个项来追加1个项),则这可能是低效的
另外,为了保持一致,指向旧数组的所有其他引用(子片)都必须更新为指向新数组中的适当位置,这意味着我们必须在切片中存储额外的信息,如开始索引。如果我们有一个子片的子片,这会变得很棘手
因此,当前的实现是有意义的
这里推荐的方法是复制子片,而不是直接处理子片,以防止此类问题。拥有副本的另一个好处是,如果原始切片很大并且不再有引用,那么它可以被垃圾收集,但是如果它有子切片,那么原始数组将被保留在内存中,直到子切片仍然引用它。这是一个关于如何在Go中实现切片的问题 看起来像:
type slice struct {
array unsafe.Pointer
len int
cap int
}
因此,切片具有长度和容量。如果尝试将项目附加到切片,使其超过当前容量,则会在下面创建一个新数组来保存新数据,但由于以前的子切片可能仍然指向旧的数组,因此它会保持原样,直到不再有对它的引用为止
现在让我们假设我们有一个切片
a
:[1,2,3,4,5,6]
和一个子切片B
,它指向a
:[4,5,6]中的最后3项
[1, 2, 3, 4, 5, 6]
^ ^
| |
| B------
|
A---------------
现在,如果我们在B
中附加一个项,那么根据您的预期行为,它也应该更新A
,因此将因此创建一个新数组。如果子片的大小与实际数组相比很小(用于从原始数组中复制1000个项来追加1个项),则这可能是低效的
另外,为了保持一致,指向旧数组的所有其他引用(子片)都必须更新为指向新数组中的适当位置,这意味着我们必须在切片中存储额外的信息,如开始索引。如果我们有一个子片的子片,这会变得很棘手
因此,当前的实现是有意义的
这里推荐的方法是复制子片,而不是直接处理子片,以防止此类问题。拥有副本的另一个好处是,如果原始切片很大并且不再有引用,那么它可以被垃圾收集,但是,如果存在子片,则原始数组将保留在内存中,直到子片仍然引用它。我认为这样做的目的是不
将视图附加到共享数据中。如果你想获取一个子片并附加到它,你就要制作一个副本。只有当你不理解片背后的简单语义时,它才会显得不一致。如果您不确定发生了什么,您可以阅读大量资源:、和上的规范、、和@user2357112谢谢,现在我已经清楚了。@JimB谢谢,我将阅读它们。这篇文章:我认为您的想法是不将视图附加到共享数据中。如果你想获取一个子片并附加到它,你就要制作一个副本。只有当你不理解片背后的简单语义时,它才会显得不一致。如果您不确定发生了什么,您可以阅读大量资源:、和上的规范、、和@user2357112谢谢,现在我很清楚了。@JimB谢谢,我将阅读它们。这篇文章: