This is the 12th day of my participation in the August More Text Challenge. For details, see: August More Text Challenge

An array of

An array is a sequence of fixed length with zero or more elements of the same data type. Arrays are rarely used directly in Go because of their fixed length. The length of the slice can be increased and decreased, and more can be used in many situations

Elements in an array are accessed by indexing from 0 to the length of the array minus 1. Go’s built-in function len returns the number of elements in an array

Var a [3]int // declare an array of integers of length 3. Println(a[0]) // get the first element in the array FMT.Println(a[len(a)-1]) // get the last element in the array V: the range of = a {FMT. Printf (" % d % d \ n ", I, v)} / / only output element for _, v: the range of = a {FMT. Printf (" % d \ n, v)}Copy the code

By default, the elements in a new array start with the zero value of the element type, or zero in the case of numbers. You can also use array literals to initialize an array based on a set of values

Var q [3] int = [3] int [3] {1, 2, 3} var r int = [3] int. {1, 2} FMT Println (r) [2] / / 0Copy the code

At the length of the array, if you use “…” So, the length of the array is determined by the number of elements that we initialize, like q, and we can initialize it this way

q := [...] Int {1,2,3} fmt.Printf("%T\n", q) // [3]intCopy the code

The array length is part of the array. So [3]int and [4]int are two different array types. The length of the array must be a constant expression, that is, the value of the expression is determined at compile time

Q: [3] = int q = {1, 2, 3} [4] int {1, 2, 3, 4} / / compile error, can't put [4] int assigned to [3] intCopy the code

An array is comparable if its element types are comparable. So that means that you can compare two arrays, using equals equals, to see if the values on both sides are exactly the same

A := [2]int{1,2} b := [... Int} {1, 2: c = [2] int. {1, 3} FMT Println (a = = b, a = = c, b = = c) / / true false falseCopy the code

Slice

Slice represents a variable length sequence of elements of the same type. Slice is usually written as []T, where the elements are of type T; It looks like an array type with no length

Arrays and slices are closely related. Slice is a lightweight data structure that can be used to access some or all of the elements of an array, which is called slice’s underlying array. Slice has three properties: pointer, length, and capacity. The pointer points to the first element of the array that can be accessed from slice. This element is not necessarily the first element of the array. Length is the number of elements in a slice and cannot exceed the size of the slice. The size of a capacity is usually the number of elements between the beginning element of a slice and the last element of the underlying array. Go’s built-in functions len and cap are used to return the length and size of a slice

A single underlying array can correspond to multiple slices, which can refer to any part of the array and overlap each other’s elements

Now declare the following array

months := [...] string{ 1:"January", 2:"February", 3:"March", 4:"April", 5:"May", 6:"June", 7:"July", 8:"August", 9:"September", 10:"October", 11:"Novemver", 12:"December" }Copy the code

The slice operator s[I :j] (where 0≤ I ≤ j ≤ cap(s)) creates a new slice that references all elements of the sequence S from I to j-1. S can be either an array, a pointer to an array, or a slice. The number of elements in the new slice is j- I. If I (s[:j]) is omitted from the expression above, then the new slice will start at 0, i.e. I =0; If j (s [I :]) is omitted, then the end index position of the new slice is len(s)-1, i.e. J =len(s). So months[1:13] refers to all valid months, and the same could be written as months[1:]. Months [:] references the entire array

Q2 := months[4:7]
summer := months[6:9]
fmt.Println(Q2) // ["April","May","June"]
fmt.Println(summer) // ["June","July","August"]
Copy the code

If the reference to slice exceeds the capacity of the referenced object, cap(s), then the application will crash. However, if the reference to a slice exceeds the length of the referenced object, that is, len(s), then the final slice will be longer than the original slice

Println(summer[:20]) Println(summer[:20]) Println(summer[:20]) EndlessSummer := summer[:5] endlessSummer := summer[:5] Println(endlessSummer) // [June July August September OctoberCopy the code

Because slice contains Pointers to array elements, passing a slice to a function modifies the elements of the underlying array inside the function. In other words, creating an array’s slice creates an alias for the array. The following function inverts the elements in integer slice

func reverse(s []int) { for i,j:=0, len(s)-1; i<j; i, j = i+1, j-1 { s[i], s[j] = s[j], s[i] } } a := [...] Int,1,2,3,4,5 {0} reverse (a) [:] FMT. Println (a) / / [5 4 3 2 1 0]Copy the code

Slice cannot be compared like an array, so you cannot use == to test whether two slices have the same elements. Why can’t slice comparisons use the == operator? There are two reasons

  • Unlike arrays, slice elements are indirect; it is possible that slice can contain itself. There are ways to deal with this particular case, but none of them are simple, efficient, or intuitive
  • Because slice elements are not direct, if the underlying array elements change, the same slice will have different elements at different times. Because hash tables (such as the MAP type of Go) only make shallow copies of the elements’ keys, this requires that the keys in the hash table must remain the same throughout the lifetime of the hash table. Because slice requires depth comparison, slice cannot be used as a map key. For reference types, such as Pointers and channels, the == operator checks for equality of reference, that is, whether they point to the same element. A similar slice equality comparison might be useful and solve the problem of Slice as a map key, but the operator == can cause problems if it behaves inconsistently with slice and arrays

So the safest approach is not to allow direct slice comparisons. The only comparison slice allows is to nil

if summer == nil {
    ......
}
Copy the code

The zero value for the slice type is NII. Slice with a nil value has no underlying array. Make ([]int,3) {make([]int,3);

var s []int // len(s) == 0, s == nil s = nil // len(s) == 0, s == nil s = []int(nil) // len(s) == 0, s == nil s = []int{} // len(s) == 0, s ! = nilCopy the code

So, if you want to check if a slice is empty, use len(s) == 0 instead of s == nil, because it’s possible for slice to be empty if s≠nil

make

The built-in function make creates a slice with a specified element type, length, and size. The capacity parameter can be omitted, in which case the length of the slice is equal to the capacity

make([]T, len)
make([]T, len, cap)
Copy the code

Make creates an unnamed array and returns a slice of it. The array is accessible only through the slice. In the first line of code above, the returned slice references the entire array. In the second line of code, Slice only references the first len elements of the array, but its capacity is the length of the array, which makes room for future slice elements

Slice the principle

For a deeper understanding of the underlying implementation of slicing, see here

Append function

The built-in function append is used to append elements to the end of slice

Var runes []rune // Define a slice of type rune For _, r := range "hello, world "{runes = append(runes, r)} fmt.Printf("%q\n", Runes)// ['h' 'e' 'l' 'l' 'o' ',' ' ' 'world' 'bound ']Copy the code

The append function is important to understand how slice works. The following example defines the appendInt method for a []int array slice

func appendInt(x []int, Y int) []int {var z []int zlen := len(x) + 1 if zlen <= cap(x) {z = x[:zlen]} else Zcap := zcap < 2 *len(x) {zcap = 3 *len(x)} z = make([]int, zcap, zcap) copy(z, z) } z[len(x)] = y return z}Copy the code

Every appendlnt call must check whether Slice still has enough capacity to store new elements in the array. If the slice is large enough, it defines a new slice (still referencing the original underlying array), copies the new y element to the new location, and returns the new slice. The input parameter slice x and the function return value slice z have the same underlying array

If slice is not large enough to hold the growing element, appendInt must create a new underlying array of large enough to store the new element, copy the element from Slice x to the array, and append the new element y to the array. The return value slice z will refer to a different underlying array than the input parameter slice x

Using a loop to copy elements may seem intuitive, but it’s easier to use the built-in copy function, which is used to copy elements for two Slices that have elements of the same type. The first parameter of the copy function is the target slice, and the second parameter is the source slice. The copy number copies elements from the source slice into the target slice. This is similar to a normal element assignment, such as dest = SRC. Different slices may correspond to the same underlying array and may even overlap elements. The copy function returns a value that returns the number of elements actually copied, which is the smaller of the two slice lengths. So there is no issue of index crossing due to element copying

For efficiency reasons, the newly created array size will be slightly larger than the minimum size needed to actually hold slice X and slice Y. The number of times the memory is allocated is reduced by doubling the size of the array each time the size is expanded, which also ensures that the append takes a fixed amount of time

The following example shows the effect of appendInt

func main() { var x, y []int for i:=0; i< 10; I++ {y = appendInt (x, I) FMT) Printf (" % d \ tcap = % d \ \ n "t % v, I, cap (y), y) x = y}} the output:  0 cap=1 [0] 1 cap=2 [0 1] 2 cap=6 [0 1 2] 3 cap=6 [0 1 2 3] 4 cap=6 [0 1 2 3 4] 5 cap=6 [0 1 2 3 4 5] 6 cap=18 [0 1 2 3  4 5 6] 7 cap=18 [0 1 2 3 4 5 6 7] 8 cap=18 [0 1 2 3 4 5 6 7 8] 9 cap=18 [0 1 2 3 4 5 6 7 8 9]Copy the code

The built-in Append function uses a more complex growth strategy than appendInt. Usually we don’t know if an append call will result in a new memory allocation, so we can’t assume that the original slice and the resulting append call point to the same underlying array, nor can we prove that they point to different underlying arrays. It is also impossible to assume that operations on elements in old slice will affect new slice elements. Therefore, the result of the append call is typically assigned again to slice that is passed into the Append function

runes = append(runes, r)
Copy the code

Append can append not only one element at a time, but also multiple elements at a time

var a []int a = append(a, 1) a = append(a, 2, 3) a = append(a, 4, 5, 6) a = append(a, a...) Println(a) //[1 2 3 4 5 6 1 2 3 4 5 6]Copy the code

Slice in place modification

The following example removes the empty string from a string and returns a new slice

package main import "fmt" func nonempty(strings []string) []string { i := 0 for _, s := range strings { if s ! = "" { strings[i] = s i++ } } return strings[:i] } func main() { data := []string{"one", "", "three"} fmt.Printf("%q\n", nonempty(data)) // ["one" "three"] fmt.Printf("%q\n", data) // ["one" "three" "three"] }Copy the code

In the example above, the input slice and the output slice have the same underlying array, thus avoiding the need to reallocate the array in the function. In this case, the elements of the underlying array are only partially modified

As you can see from the print above, the value of data has changed. So usually we’ll say data = nonempty(data)

Slice implements a stack

Stack := []int stack = append(stack, v) // push v top := stack(len(stack)-1)// Stack = stack[:len(stack)-1] // popCopy the code

To remove an element from the middle of the slice and preserve the order of the remaining elements, use the copy function to move the higher-indexed element forward to cover the location of the removed element

func remove(slice []int, i int) []int { copy(slice[i:], Slice [I + 1:]) return slice [: len (slice) - 1]} func main () {s: int [] = {5,6,7,8,9} FMT. Println (remove (s, 2)) / / [9] 5 6 8}Copy the code

If you do not need to maintain the original order, you can simply assign the last element of slice to the location of the element to be removed

reference

Go Programming Language — Alan A. A. Donovan

Go Language Learning Notes — Rain Marks