英文:
Out growing Slice and Underlying array
问题
我有一个数组和一个指向它的切片,如下所示:
package main
import "fmt"
func main() {
array_str := []string{"0a","1b","2c","3d","4e"}
slice_str := array_str[1:4]
fmt.Println("Initially :")
fmt.Println("Printing 1 :Array :", array_str)
fmt.Println("Printing 1 :Slice:", slice_str)
//Step 1.修改切片,反映在数组中
fmt.Println("\nAfter Alteration:")
slice_str[0] = "alterd_1b"
fmt.Println("Printing 2 :Array :", array_str)
fmt.Println("Printing 2 :Slice:", slice_str)
fmt.Println("len of slice_str:", len(slice_str), "cap of slice_str:", cap(slice_str), "len of array_str:", len(array_str))
//Step 2.向切片追加元素,反映在数组中
slice_str = append(slice_str, "apnded_elemnt")
fmt.Println("\nAfter Appending:")
fmt.Println("Printing 3 :Array :", array_str) //"4e"在数组中被替换为"apnded_elemnt"!!
fmt.Println("Printing 3 :Slice:", slice_str)
fmt.Println("len of slice_str:", len(slice_str), "cap of slice_str:", cap(slice_str), "len of array_str:", len(array_str))
//Step 3.再次向切片追加元素,使切片的长度进一步增长到底层数组
slice_str = append(slice_str, "outgrown_elemnt")
fmt.Println("\nAfter OUT GROWING:")
fmt.Println("Printing 4 :Array :", array_str) //显然,超出范围的元素没有添加到数组中,这是正常的
fmt.Println("Printing 4 :Slice:", slice_str)
fmt.Println("len of slice_str:", len(slice_str), "cap of slice_str:", cap(slice_str), "len of array_str:", len(array_str))
//为什么容量在这里变成了8?
//Step 4.现在更改切片元素,该元素在数组范围内,以验证它是否反映在数组中:
fmt.Println("\nAfter Post out grown Alteration:")
slice_str[0] = "again_alterd_1b"
fmt.Println("Printing 2 :Array :", array_str) //切片的更改不会反映在数组中。为什么?
fmt.Println("Printing 2 :Slice:", slice_str)
}
问题:
-
在第3步中,为什么切片的
cap
从4跳到8? -
在第4步中,当切片超出范围后,对于数组范围内的元素进行的更改不会反映在数组中,反之亦然。为什么在切片超出范围后不会发生这种情况?切片超出范围时实际发生了什么?
英文:
I have an array and a slice pointing to it, like shown as follows:
package main
import "fmt"
func main() {
array_str := []string{"0a","1b","2c","3d","4e"}
slice_str:=array_str[1:4]
fmt.Println("Initially :")
fmt.Println("Printing 1 :Array :",array_str)
fmt.Println("Printing 1 :Slice:",slice_str)
//Step 1.Changing Slice and it get reflected in array
fmt.Println("\nAfter Alteration:")
slice_str[0]="alterd_1b"
fmt.Println("Printing 2 :Array :",array_str)
fmt.Println("Printing 2 :Slice:",slice_str)
fmt.Println("len of slice_str:",len(slice_str)," cap of slice_str:",cap(slice_str),"len of array_str:",len(array_str))
//Step 2.appending to slice and it get reflected
slice_str = append(slice_str,"apnded_elemnt")
fmt.Println("\nAfter Apending:")
fmt.Println("Printing 3 :Array :",array_str)//"4e" is replaced with "apnded_elemnt" in array !!
fmt.Println("Printing 3 :Slice:",slice_str)
fmt.Println("len of slice_str:",len(slice_str)," cap of slice_str:",cap(slice_str),"len of array_str:",len(array_str))
//Step 3.Again appending to slice so that lentght of slice is growing further to underlaying array
slice_str = append(slice_str,"outgrown_elemnt")
fmt.Println("\nAfter OUT GROWING:")
fmt.Println("Printing 4 :Array :",array_str)//obeviously out grown elemnt not added to array that is fine
fmt.Println("Printing 4 :Slice:",slice_str)
fmt.Println("len of slice_str:",len(slice_str)," cap of slice_str:",cap(slice_str),"len of array_str:",len(array_str))
//How Capacity Become 8 here ???
//Step 4 .Now Changing Slice element which is in Range of array to verify it reflect on array:
fmt.Println("\nAfter Post out grown Alteration:")
slice_str[0]="again_alterd_1b"
fmt.Println("Printing 2 :Array :",array_str)//Change in slice is not reflectd in array .Why ?
fmt.Println("Printing 2 :Slice:",slice_str)
}
Playground: http://play.golang.org/p/3z52HXHQ7s
Questions:
-
In Step 3: why does
cap
of the slice jumped from 4 to 8? -
In Step 4: after the slice is out grown, changes to the element of the slice, which is in the range of the array, is not reflected to the array and vice versa. Why is it not happening after it is grown out? What actually happens when the slice grows out?
答案1
得分: 5
请看这里:http://blog.golang.org/slices
简短回答:1)切片在长度较短时通过倍增来增长。如果你追加一次,可能还会追加第二次,这样可以避免分配内存。2)这就是切片增长的工作原理。数组无法增长,所以会分配一个新的更大的数组,将旧数组复制过去,然后将一个指向更大数组的切片返回给你。
(golang.org网站上的文档非常有帮助,易读、简短而准确。我建议先在golang.org上查看,然后再在这里提问。)
英文:
See here: http://blog.golang.org/slices
Short answers: 1) it grows by doubling (while short). If you append once you might append a second time too and this avoids allocations. 2) That's how slice growing works. An array cannot grow, so a new, larger array is allocated, the old one copied and you are handed a slice pointing to the larger copy.
(The documentation on the golang.org website is really helpful, readable, short and precise. I'd like to recommend to look at golang.org first before asking here.)
答案2
得分: 2
容量乘以2是因为它更节省资源。事实上,内存分配非常消耗资源,最好一次性分配大量内存,而不是每次都分配所需的内存。
让我们先用一个简单的示例来比较,使用字符串连接的方式:每次,Go 只分配所需的内存。
var n = time.Now()
var s = ""
for i := 0; i < 1000000; i++ {
s += "a"
}
fmt.Println(time.Now().Sub(n))
// 47.7秒
现在,我们做同样的事情,但这次使用 bytes.Buffer
类型:
var n = time.Now()
var b = bytes.NewBufferString("")
for i := 0; i < 1000000; i++ {
b.WriteString("a")
}
fmt.Println(time.Now().Sub(n))
// 18.5毫秒
区别在于 Buffer
分配内存的方式:当容量不足时,它会分配当前容量的两倍:
buf = makeSlice(2*cap(b.buf) + n)
这对切片也是一样的(我只是找不到证明的源代码...)。所以是的,你可能会损失一些空间,但这是为了更高的效率!
对于你的第二个问题,对我来说有点棘手,所以我希望 @Volker 的回答对你足够清晰!
英文:
The capacity is multiplied by 2 because it is less consuming. Indeed, memory allocation is very consuming, and it's better to allocate a lot of memory a single time than exactly what is needed every time.
Let's just compare, first with a simple example using the concatenation: every time, Go allocates just what is needed.
var n = time.Now()
var s = ""
for i := 0; i < 1000000; i++ {
s += "a"
}
fmt.Println(time.Now().Sub(n))
// 47.7s
Now, let's do the same but this time using the bytes.Buffer
type:
var n = time.Now()
var b = bytes.NewBufferString("")
for i := 0; i < 1000000; i++ {
b.WriteString("a")
}
fmt.Println(time.Now().Sub(n))
// 18.5ms
The difference is the way Buffer
allocates memory: when there is not enough capacity, it allocates twice the current capacity:
buf = makeSlice(2*cap(b.buf) + n)
This works the same with slices (I was just not able to find the source code to prove it...). So yes, you may be losing some space, but this is for a much better efficiency!
You're second question is a bit more tricky for me, so I hope @Volker's answer will be clear enough for you !
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论