Summary of Slice learning in Go language

  • 2020-05-09 18:44:38
  • OfStack

concept

Slice Array encapsulation, slice is the underlying array is stored in the memory essence array, embodied in contiguous memory block, Go array definitions of language, have a fixed length, in use process does not change its length, but Slice can be used as a variable length arrays, the most critical, is in the process of using an array value, will be assigned to a new one array variable or as method parameters, is the source array in memory 1 completely copy, rather than a reference source array in memory address, In order to meet the reuse of memory space and the consistency of the value of the array elements of the application requirements, Slice emerged, each Slice are source array in memory address of a reference, the source array can derive multiple Slice, Slice can also continue to derive Slice, and memory, always only source array, of course, there are exceptions, back to say again.

usage

1. The definition of Slice

Slice can be defined in two ways, one is a derivative in the source array, 1 kind is through make function definition, essentially is one kind, is in memory of the way through the array initialization in opening up 1 piece of memory, can be divided into several small pieces used to store an array element, then Slice to reference the entire or partial array element.
Directly initialize 1 Slice:


 s := []int{1, 2, 3}

Some students think that this method is to define and initialize an array. In fact, this method is to construct an array containing 3 elements in memory, and then assign the application value of this array to s and Slice. The difference is made through the following array definition:


 a := [3]int{1, 2, 3}
 b := [...]int{1, 2, 3}
 c := []int{1, 2, 3}
 fmt.Println(cap(a), cap(b), cap(c))
 a = append(a, 4)//Error:first argument to append must be slice; have [3]int
 b = append(b, 4)//Errot:first argument to append must be slice; have [3]int
 c = append(c, 4)// Normal, indicating variables c is Slice type

As you can see, the rule of array definition is emphasized: the length and type must be specified. If the array length is automatically calculated according to the actual number of elements, the [...] should be used. Define, not just use [].

Building Slice by slicing from the array:


 a := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
 s := a[2:8]
 fmt.Println(s) // Output: [3 4 5 6 7 8]

Define 1 array a, intercept the subscript between 2 and 8 (including 2 but not including 8), and build 1 Slice.

Definition by make function:


 s := make([]int, 10, 20)
 fmt.Println(s) // Output: [0 0 0 0 0 0 0 0 0 0]

The first parameter of the make function represents the type of array constructed, the second parameter is the length of the array, and the third parameter is optional, which is the capacity of slice. The default is the value of the second parameter.

2. Length and capacity of Slice

Slice has two confusing concepts: length and capacity. What is length? This length, along with the length of the array, is a concept that initializes the number of elements that actually exist in memory. What is capacity? If create Slice through make function parameter specifies the capacity, the memory manager will according to the specified capacity of the value of the first division 1 piece of memory space, and then in the storage array element, the spare part is in the idle state, on the Slice additional elements, will be on the first free memory, if you add the number of parameters than the capacity value, the memory manager can redraw 1 piece capacity value is the size of the volume value * 2 memory space, and so on. The advantage of this mechanism is that it can improve the performance of the computation, because the repartition of memory will reduce the performance.


 a := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
 s := a[0:]
 s = append(s, 11, 22, 33)
 sa := a[2:7]
 sb := sa[3:5]
 fmt.Println(a, len(a), cap(a))    // Output: [1 2 3 4 5 6 7 8 9 0] 10 10
 fmt.Println(s, len(s), cap(s))    // Output: [1 2 3 4 5 6 7 8 9 0 11 22 33] 13 20
 fmt.Println(sa, len(sa), cap(sa)) // Output: [3 4 5 6 7] 5 8
 fmt.Println(sb, len(sb), cap(sb)) // Output: [6 7] 2 5

As you can see, the array len and cap are always equal, and are specified at the time of definition and cannot be changed. Slicing s refers to all the elements of this array. The initial length and capacity are both 10. After adding 3 more elements, the length becomes 13 and the capacity is 20. Section sa intercepts an array fragment with subscript 2 to 7, with a length of 5 and a capacity of 8. The rule for changing this capacity is to subtract the initial subscript from the original capacity. At this time, if an element is appended, the value existing in the original memory address will be overwritten. Slice sb intercepts a fragment of an array of slices sa subscript 3 to 5. Note that the subscript here refers to the subscript of sa, not the subscript of the source array. The length is 2 and the capacity is 8-3=5.

3.Slice is the reference type

As mentioned above, Slice is a reference to the source array. Changing the value of the element in Slice is essentially changing the value of the element in the source array.


 a := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
 sa := a[2:7]
 sa = append(sa, 100)
 sb := sa[3:8]
 sb[0] = 99
 fmt.Println(a)  // Output: [1 2 3 4 5 99 7 100 9 0]
 fmt.Println(sa) // Output: [3 4 5 99 7 100]
 fmt.Println(sb) // Output: [99 7 100 9 0]

As you can see, both the append operation and the assignment operation affect the source array or other elements that reference Slice in the same array. When Slice makes an array reference, it actually points to the address of a specific element in memory, such as the memory address of a set of Numbers, which is actually the memory address of the first element in the array, as is Slice.


 a := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
 sa := a[2:7]
 sb := sa[3:8]
 fmt.Printf("%p\n", sa)   // Output: 0xc084004290
 fmt.Println(&a[2], &sa[0])      // Output: 0xc084004290 0xc084004290
 fmt.Printf("%p\n", sb)   // Output: 0xc0840042a8
 fmt.Println(&a[5], &sb[0])      // Output: 0xc0840042a8 0xc0840042a8

4. "accident" in Slice reference delivery

We 1 straight in the above said Slice is a reference type, are all in memory with 1 piece of memory, but in practice, but sometimes happen "accidental", this kind of situation only when sliced like append elements, Slice processing mechanism is such, when Slice capacity and idle append elements come in to be able to direct use of idle capacity space, but as the number of elements in the append come in over the original capacity value specified, the memory manager is to create a larger memory space, It is used to store the extra elements, and it will copy the original elements and put them into the newly created memory space.


 a := []int{1, 2, 3, 4}
 sa := a[1:3]
 fmt.Printf("%p\n", sa) // Output: 0xc0840046e0
 sa = append(sa, 11, 22, 33)
 fmt.Printf("%p\n", sa) // Output: 0xc084003200

You can see that after performing the append operation, the memory address has changed, indicating that it is no longer a reference pass.


Related articles: