When a large number of variables of the same type are operated in a program, it is often necessary to use the power of containers to facilitate the storage and operation of data. The GO language standard library provides common container implementations. For example, fixed-size arrays, dynamically expanded slices, two-way lists and key-value dictionaries. The following describes the basic use of arrays and slicing.
An array of
An array is a contiguous chunk of memory that stores a fixed length, and its size is fixed when it is declared. Although the size cannot change, the members of the array can. Arrays are declared as follows:
var name [size]Type
Copy the code
The size of the array must be specified, either as a constant or as an expression. The size must be determined statically at compile time. The size cannot be specified dynamically.
Arrays can be initialized at declaration time using an initializer list, or data members can be accessed and assigned by subscript, as shown below.
func main() { var animals1 [3]string animals1[0] = "cat" animals1[1] = "dog" animals1[2] = "rabbit" fmt.Println(animals1) //[cat dog rabbit] animals2 := [...] string{"duck", "chicken", "cat"} fmt.Println(animals2) //[duck chicken cat] }Copy the code
When initializing an array using an initializer, note that the size of [] must be the same as the number of array members in {}. Let the compiler determine the size of the array based on the number of members of {}.
animals3 := new([3]string)
animals3[0] = "dog"
animals3[1] = "cat"
animals3[2] = "duck"
fmt.Println(*animals3) // [dog cat duck]
Copy the code
slice
A slice is a reference to a contiguous fragment of an array, which is a variable-capacity sequence. Its internal structure includes the underlying array pointer, size, and capacity. It restricts reading and writing to data to a specified area by referring to the underlying array with a pointer.
The way slices are generated
1. Generate slices from the native array and modify slice members
Generate a slice from the original array, then the generated slice pointer points to the original array in the following format:
slice := source[begin:end]
Copy the code
Source represents the original array to generate the slice, begin represents the beginning of the slice, and end represents the end of the slice (excluding the end bit), which is consistent with Python syntax. The following is an example:
a := [...] Int {0,1,2,3,4,5,6} aSilce := a[0:2] FMT.Printf("aSilce value is %v\naSilce len is %v\naSilce cap is %v\n", aSilce, Len (aSilce), cap(aSilce)) // The output is: aSilce value is [0 1] aSilce len is 2 aSilce cap is 7Copy the code
In this slice, we can only access values within the length of the slice. If the access index exceeds the length of the slice, the compiler will throw an exception with out-of-bounds indices. If we modify a member in a slice, the slice will be used on a reference to the original array, so the original array value will change.
aSilce[0] = 4
fmt.Printf("aSlice value is %v\nSource array value is %v", aSilce,a)
// aSlice value is [4 1]
// Source array value is [4 1 2 3 4 5 6]
Copy the code
The above statement actually has some problems. If the current slice capacity can hold more elements, that is, the size is smaller than CAP, the adding operation points to the original array for modification. When the slice capacity is insufficient to hold more elements, a new address space will be applied.
2. Dynamically create slices
You can use the make function to create slices dynamically, specifying the length and capacity of slices during creation, as shown in the following example:
make([]T, size, cap)
Copy the code
T is the member type of the slice, size is the length of the current slice, and CAP is the capacity allocated by the current slice. The following is an example:
sli = make([]int, 2, 4) fmt.Printf("sli value is %v\n", sli) fmt.Printf("sli len is %v\n", Len (sli)) fmt.Printf("sli cap is %v\n", cap(sli)) // Sli value is [0 0] sli len is 2 sli cap is 4Copy the code
You can see that the members in the new slice created by the make function are initialized to the initial values of the type.
3. Declare a new slice
Declaring a slice is similar to initializing an array, but you do not need to specify its size, otherwise it becomes an array.
var name []T
Copy the code
At this time, the declared slice has no memory allocated. We can initialize it while declaring the slice, as shown in the following example:
Sli := [] int{1, 2, 3} FMT.Printf("sli value is %v,len is %v,cap is %v",sli, len(sli),cap(sli)) sli value is [1 2 3],len is 3,cap is 3Copy the code
4. Add elements to the slice
The GO language provides built-in append functions to dynamically add elements to a slice, which returns a new slice.
package main import "fmt" func main() { arr1 := [...] Int arr2: {1, 2, 3, 4} = [...]. Int {1,2,3,4} sli1 := arr1[0:2] // length is 2, capacity is 4 sli2 := arr2[2:4] // length is 2, Printf("sli1 pointer is %p, len is %v, cap is %v, value is %v\n", &sli1, len(sli1), cap(sli1), sli1) fmt.Printf("sli2 pointer is %p, len is %v, cap is %v, value is %v\n", &sli2, len(sli2), cap(sli2), sli2) newSli1 := append(sli1, 5) fmt.Printf("newSli1 pointer is %p, len is %v, cap is %v, value is %v\n", &newSli1, len(newSli1), cap(newSli1), newSli1) fmt.Printf("source arr1 become %v\n", arr1) newSli2 := append(sli2, 5) fmt.Printf("newSli2 pointer is %p, len is %v, cap is %v, value is %v\n", &newSli2, len(newSli2), cap(newSli2), newSli2) fmt.Printf("source arr2 become %v\n", arr2) arr3 := [...] Int {1,2,3,4} sli3 := arr3[0:2:2] // length = 2, Printf("sli3 pointer is %p, len is %v, cap is %v, value is %v\n", &sli3, len(sli3), cap(sli3), Printf("newSli3 pointer is %p, len is %v, cap is %v, value is %v\n", &newSli3 := append(sli3,5) fmt.Printf("newSli3 pointer is %p, len is %v, cap is %v, value is %v\n", &newSli3, len(newSli3), cap(newSli3), newSli3) fmt.Printf("source arr3 become %v\n", arr3) }Copy the code
The output is as follows:
sli1 pointer is 0xc00000c060, len is 2, cap is 4, value is [1 2]
sli2 pointer is 0xc00000c080, len is 2, cap is 2, value is [3 4]
newSli1 pointer is 0xc00000c0e0, len is 3, cap is 4, value is [1 2 5]
source arr1 become [1 2 5 4]
newSli2 pointer is 0xc00000c120, len is 3, cap is 4, value is [3 4 5]
source arr2 become [1 2 3 4]
sli3 pointer is 0xc00000c160, len is 2, cap is 2, value is [1 2]
newSli3 pointer is 0xc00000c1a0, len is 3, cap is 4, value is [1 2 5]
source arr3 become [1 2 3 4]
Copy the code
We found that slI with sufficient capacity directly overwrites the new element added by append into the original array arr1, while SLI2 with insufficient capacity is expanded by applying for a new underlying array instead of operating on the original array. Special attention should be paid to the actual use.