The length len and capacity cap of Go slice Slice

0x00 Introduction
Go Language Tour Example Slice Length and Capacity

Slices have length and capacity.
The length of a slice is the number of elements it contains.
The capacity of a slice is the number from its first element to the end of its underlying array elements.
The length and capacity of a slice s can be obtained by the expressions len(s) and cap(s).

package main

import "fmt"

func main() {
    s := []int{2, 3, 5, 7, 11, 13}
    printSlice(s)

    // Intercept the slice so that its length is 0
    s = s[:0]
    printSlice(s)

    // extend its length
    s = s[:4]
    printSlice(s)

    // Discard the first two values
    ​​s = s[2:]
    printSlice(s)
}

func printSlice(s []int) {     fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s) } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 len=6 cap=6 [2 3 5 7 11 13] len=0 cap=6 [] len=4 cap=6 [2 3 5 7] len =2 cap=4 [5 7] 1 2 3 4 0x01 The program runs 1, the first output is [2,3,5,7,11,13], the length is 6, and the capacity is 6;



































2. The left and right pointers point to s[0] at the same time, so the length is 0 and the capacity is 6;

3. The left pointer points to s[0], and the right pointer points to s[4]. Since the concept of slicing only includes elements on the left and not elements on the right, the length is 4, but the left pointer is at s[0], and has gone through 0 elements, so the capacity is still 6;

4. On the basis of slicing in step 3, the left pointer points to s[2], and the right pointer points to the rightmost, so the length is 2. Since the left pointer has passed through two elements, there are still 4 elements left from the rightmost, so Capacity is 4.


The most difficult thing to understand in this article is the capacity of the slice. We can regard the capacity as the total length minus the element value that the left pointer has passed, for example: s[:0
] ——> cap = 6 - 0 =6;
s[ 2:] ——> cap = 6 - 2 = 4.

0x02 Inside the slice
A slice is a description of an array segment. It contains a pointer to the array, the length of the segment, and capacity (the maximum length of the segment).
[External link picture transfer failed, the source site may have an anti-leeching mechanism, it is recommended to save the picture and upload it directly (img-Yci8EQBR-1585976257296)(https://blog.go-zh.org/go-slices-usage-and -internals_slice-struct.png)]
The structure of the slice variable s created by make([]byte, 5) is as follows:
[External link picture transfer failed, the source site may have an anti-leeching mechanism, it is recommended to save the picture and upload it directly (img-kR7U0t8j-1585976257297)(https://blog.go-zh.org/go-slices-usage-and-internals_slice-1.png)]
The length is the number of elements referenced by the slice. Capacity is the number of elements of the underlying array (starting at the slice pointer). The length and capacity and area will be explained in the next example.
Let's go ahead and slice s, looking at the slice's data structure and the underlying array it references:

s = s[2:4]
1
[Failed to save the external link picture, the source site may have an anti-leeching mechanism, it is recommended to save the picture and upload it directly (img-tZTU83Aa-1585976257299)(https://blog.go-zh. org/go-slices-usage-and-internals_slice-2.png)]
slice operations do not copy the element pointed to by the slice. It creates a new slice and reuses the underlying array of the original slice. This makes slicing operations as efficient as array indexing. Therefore, modifying elements through a new slice will affect the corresponding elements of the original slice.

d := []byte{'r', 'o', 'a', 'd'}
e := d[2:]
// e == []byte{'a', 'd'}
e[ 1] = 'm'
// e == []byte{'a', 'm'}
// d == []byte{'r', 'o', 'a', 'm'}
1
2
3
4
5
6
The length of the previously created slice s is less than its capacity. We can grow the slice's length to its capacity:

s = s[:cap(s)]
1
[External link picture transfer failed, the source site may have an anti-leeching mechanism, it is recommended to save the picture and upload it directly (img-t0aIOp7h-1585976257306)(https://blog.go- en.org/go-slices-usage-and-internals_slice-3.png)]
slices cannot grow beyond their capacity. Growing beyond the capacity of the slice will cause a runtime exception, just as an index out of bounds for a slice or array would cause an exception. Likewise, an index less than zero cannot be used to access elements preceding the slice.

0x03 Intercept append
package main

import "fmt"

func main() {
    s := []int{2, 3, 5, 7, 11, 13}
    printSlice(s)

    // Intercept the slice so that its length is 0
    s = s[:0]
    printSlice(s)

    // extend its length
    s = s[:5]
    printSlice(s)
    
    // add s for the first time
    = append(s, 1)
    printSlice(s)
    
    // add
    s for the second time = append(s, 1)
    printSlice( s)
}

func printSlice(s []int) {
    fmt.Printf("len=%d cap=%d ptr=%p %v\n", len(s), cap(s), s, s)
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
len=6 cap=6 ptr=0x450000 [2 3 5 7 11 13]
len=0 cap =6 ptr=0x450000 []
len=5 cap=6 ptr=0x450000 [2 3 5 7 11]
len=6 cap=6 ptr=0x450000 [2 3 5 7 11 1]
len=7 cap=12 ptr=0x44e030 [ 2 3 5 7 11 1 1]
1
2
3
4
5
When appending for the first time, the length does not exceed the capacity, so the capacity does not change.
When appending for the second time, the length exceeds the capacity, and the capacity will be doubled at this time.
At the same time, the added slice address is different from the original one, that is to say:
the append operation may cause two Slice variables that originally used the same underlying array to use different underlying arrays.

0x04 Intercept append
package main

import "fmt"

func main() {
    s := []int{2, 3, 5, 7, 11, 13}
    printSlice(s)

    // Intercept the slice so that its length is 0
    s = s[:0]
    printSlice(s)

    // extend its length
    s = s[:4]
    printSlice(s)
    
    // slice after append
    s = append(s, 4)
    s = s[:6]
    printSlice(s)
}

func printSlice(s []int) {     fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s) } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 len=6 cap=6 [2 3 5 7 11 13] len=0 cap=6 [] len=4 cap = 6 [2 3 5 7] len=6 cap=6 [2 3 5 7 4 13] 1 2 3 4 Verify a problem here. After appending a 4 on the basis of [2 3 5 7], get the slice at full capacity and find that the last 13 is still there. It's just that 11 is replaced by 4. This shows that the writing of append is to overwrite the data behind the slice one by one.




































0x05 slice copy
is used to copy the content from one array slice to another array slice. If the two added array slices are not the same size, they will be copied according to the number of elements of the smaller array slice.

package main

import "fmt"

func main() {
    slice1 := []int{1, 2, 3, 4, 5}
    slice2 := []int{5, 4, 3}
    slice3 := []int{5, 4, 3}

    copy(slice2, slice1) // will only copy the first 3 elements of slice1 to slice2
    printSlice(slice2)
    
    copy(slice1, slice3) // will only copy the 3 elements of slice3 to the first 3 positions of slice1
    printSlice(slice1 )
}

func printSlice(s []int) {     fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s) } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 len=3 cap=3 [1 2 3] len=5 cap=5 [5 4 3 4 5] 1 2 To increase the capacity of a slice you must create a new, larger capacity slice, and then copy the contents of the original slice to the new slice. The whole technique is some common implementation of languages ​​that support dynamic arrays. The following example doubles the capacity of slice s, first creates a new slice t with twice the capacity, copies the elements of s to t, and then assigns t to s:

























t := make([]byte, len(s), (cap(s)+1)*2) // +1 in case cap(s) == 0 for i := range s { t[
i         ] = s[i] } s = t 1 2 3 4 5 The operation of copying in the loop can be replaced by the copy built-in function. The copy function copies the elements of the source slice to the destination slice. It returns the number of copied elements.








func copy(dst, src []T) int
1
The copy function supports copying between slices of different lengths (it only copies length elements of the shorter slice). Additionally, the copy function correctly handles cases where the source and destination slices overlap.

Using the copy function, we can simplify the code snippet above:

t := make([]byte, len(s), (cap(s)+1)*2)
copy(t, s)
s = t
1
2
3
A common operation is to append data to the end of the slice. The following function appends elements to the end of the slice, increases the slice's capacity if necessary, and returns the updated slice:

func AppendByte(slice []byte, data ...byte) []byte {
    m := len(slice)
    n := m + len(data)
    if n > cap(slice) { // if necessary, reallocate
        // allocate double what's needed, for future growth.
        newSlice := make([]byte, (n+1)*2)
        copy(newSlice, slice)
        slice = newSlice
    }
    slice = slice[0:n]
    copy(slice[m:n], data)
    return slice
}
1
2
3
4
5
6
7
8
9
10
11
12
13
下面是 AppendByte 的一种用法:

p := []byte{2, 3, 5}
p = AppendByte(p, 7, 11, 13)
// p == []byte{2, 3, 5, 7, 11, 13}
1
2
3
similar The AppendByte function is useful because it provides full control over the growth of the slice. According to the characteristics of the program, it may be desirable to allocate smaller or larger blocks, or to allocate more than a certain size.

0x06 Summary
The length and capacity of a slice are different. The length represents the distance from the left pointer to the right pointer, and the capacity represents the distance from the left pointer to the end of the underlying array.
The expansion mechanism of the slice, when appending, if the length exceeds the capacity after the increase, the capacity will be increased by 2 times, and the underlying array will be transformed at the same time.
The slice append mechanism is to overwrite and write the data behind the slice one by one.
The slice copy mechanism copies the elements of the smaller array slice.
0x07 Refer to
the append of Slice in Golang to explain the details of
Golang arrays and Slices, and the traps of the append function.
Go guide_The length and capacity of slices
Go slices: usage and essence
—————————————————
Copyright statement : This article is an original article by CSDN blogger "Shower Scarecrow", following the CC 4.0 BY-SA copyright agreement, please attach the original source link and this statement for reprinting.
Original link: https://blog.csdn.net/u013474436/article/details/88770501

Guess you like

Origin blog.csdn.net/qq_32907195/article/details/114115353