Reputation: 1073
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?
Upvotes: 1
Views: 910
Reputation: 20305
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 !
Upvotes: 2
Reputation: 42413
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.)
Upvotes: 5