How to better learn slicing data types in Golang

meaning

Slice is a special array. Is a reference to a contiguous fragment of an array, so a slice is a reference type. Slices can be part of an array or a subset of items identified by the start and end indexes. The slice is a bit like the pointer in C language. The pointer can perform operations, but the cost is that the memory operation is out of bounds. The slice increases the size based on the pointer and restricts the memory area corresponding to the slice. During the use of the slice, it is impossible to manually adjust the internal address and size of the slice, so the slice is safer and more powerful than the pointer.

definition

The slice definition is divided into three forms. Generate from array, generate from slice, and newly define a slice.

Three elements

1. Start position: the start position of the slice reference array.

2. Size: the number of elements in the slice. The size in the slice cannot exceed the capacity. You can use the len() function to count the size of the slice.

3. Capacity: the maximum number of elements that can be stored in the slice. If there is not enough space to accommodate enough elements, the slice will be dynamically "expanded", and the length of the new slice will change. Generally, the expansion of slices is twice the capacity before expansion. You can use the cap() function to count the slice capacity.

The difference between slice and array

  1. A slice is a continuous reference to an array. The initial position of the slice points to the memory address of the array. If the value of the slice changes, the corresponding value of the array will change accordingly.
  2. The length of the slice is dynamic and is essentially a variable dynamic array. The length of the array is determined when it is defined. The length of the array cannot be modified later.
  3. The length of the slice can be dynamically expanded [as mentioned above].
  4. The slice itself does not save data, it is only the representation of the underlying array. Any changes made to the slice are reflected in the underlying array.
package main
import (
 "fmt"
)
func main() {
 numa := [3]int{78, 79, 80}
 nums1 := numa[:]
 nums2 := numa[:]
 fmt.Println("array before change 1", numa)
 nums1[0] = 100
 fmt.Println("array after modification to slice nums1", numa)
 nums2[1] = 101
 fmt.Println("array after modification to slice nums2", numa)
}
// output
array before change 1 [78 79 80]
array after modification to slice nums1 [100 79 80]
array after modification to slice nums2 [100 101 80]

When multiple slices share an underlying array, the changes of each slice will be reflected in the underlying array.

Sample code

// Defining slices through arrays
var array1 = [3]int{1, 1, 3}
fmt.Println("The elements of the array are:", array1)
slice1 := array1[0:2]
slice1[1] = 11111
fmt.Println("The elements of the array are:", array1)
// Output results
 The elements of the array are: [1 1 3]
The elements of the array are: [1 11111 3]
``
## Slice definition classification

### Array generation slice

#### Define syntax

```go
slice[Starting position:End position]

1.slice: object representing slice. For example, if a slice is generated from an array, slice is the defined array name.

2. Starting position: starting from the subscript of an element in the array, starting from 0 by default.

3. End position: the end position of the slice. That is, the subscript position of an element of the array. It should be noted that the open interval is taken here. If you need to get the last element of the array, the end position is the length of the array + 1.

4. Length of slice: (end position of slice - start position of slice).

Sample code

// Defining slices through arrays
array := []string{"A","B","C","D","E","F","G","H","I","G","K","L"}
slice := array(0:5)
// The print result is
[A B C D E]

// Create slices using make
slice1 := make([]string, 2, 3)
slice1[0] = "1"
fmt.Println(slice1)

slice2 := make([]string, 2, 3)
fmt.Println(slice2)

fmt.Println("The length of the slice is", len(slice1))
fmt.Println("The capacity of the slice is", cap(slice1))
// output
[1 ]
[ ]
The length of the slice is 2
 The capacity of the slice is 3

Slice index

1. The start position and end position of the slice are omitted. By default, it is intercepted from the start position of the array to the end position of the array + 1. The result is the same slice as the contents of the array, representing the original slice.

// Slice definitions omit start and end positions
array := []string{"A","B","C","D","E","F","G","H","I","G","K","L"}
fmt.Println("Both the start and end positions are default",array[:])
// output
 Both the start and end positions are default [A B C D E F G H I G K L]

2. The start position and end position of the slice are not omitted. Cut according to the start position and end position.

// Neither the start position nor the end position is omitted
array := []string{"A","B","C","D","E","F","G","H","I","G","K","L"}
slice := array(0:5)
// output
[A B C D E]

3. The start position is omitted and the end position is not omitted. By default, the array is cut from the start position to the end position.

// The start position is omitted and the end position is not omitted
array := []string{"A","B","C","D","E","F","G","H","I","G","K","L"}
slice := array(:5)
// output
[A B C D E]

4. The starting position is not omitted and the ending position is omitted. By default, it is stolen from the specified starting position of the array to the last position of the array (array length + 1).

// The start position is not omitted and the end position is omitted
array := []string{"A","B","C","D","E","F","G","H","I","G","K","L"}
fmt.Println("Default end position:",array[2:])
// The print result is
 Default end position: [C D E F G H I G K L]

5. The start position and end position of the slice cannot exceed the range of the array.

array := []string{"A","B","C","D","E","F","G","H","I","G","K","L"}
fmt.Println("section",array[-1:100])
// The print result is
invalid slice index -1 (index must be non-negative)

6. The start position and end position of the slice are both 0, and an empty slice is obtained, indicating that the slice is empty. It is generally used for slice reduction.

// Both start and end positions are 0
array := []string{"A","B","C","D","E","F","G","H","I","G","K","L"}
fmt.Println("section",array[0:0])
// The print result is
 section: []

Direct declaration slice

In addition to generating slices from the original array or slice, you can also declare a new slice. Each type can have its slice type, representing a continuous collection of multiple elements of the same type. Therefore, the slice type can also be declared.

Define syntax

// It can also be in the form of an empty array
var slice []type

1.slice is the name of the slice.

2.type is the data type of the slice.

Code example

// Declare an integer slice
var slice1 []int
// Initialize a slice
var slice2 []int = []int{}

Using make to define slices

In addition to the above methods, if you need to create a slice dynamically, you can use the make() built-in function.

Define syntax

make([]type, size, cap)

1.type is the data type of the slice.

2.size is the size of the slice.

3.cap is the capacity of the slice.

The size of the slice cannot exceed the capacity. The capacity represents the maximum number of elements in the slice, and the size of the slice represents the actual number of elements. For example, 30 people can sit in a classroom. Now there are 10 people. Here, 10 means size and 30 means cap.

Code example

// Define slice
slice1 := make([]string, 2, 3)
slice1[0] = "1"
fmt.Println(slice1)
// Print the following results
[1 ]

If the corresponding subscript is not assigned a value during slice copying, a value is assigned by default according to the data type. For example, slince1 defined above has two lengths, but only the value with subscript 0 is assigned. Therefore, the value with subscript 1 is assigned by default according to the string type of data type.

Common operation

Length calculation

The slice length is calculated using len().

// Calculate slice length
slice1 := make([]string, 2, 3)
fmt.Println("The length of the slice is", len(slice1))
// The print result is
 The length of the slice is 2

Capacity calculation

Slice capacity is calculated using cap().

// Calculate slice length
slice1 := make([]string, 2, 3)
fmt.Println("The length of the slice is", cap(slice1))
// The print result is
 The capacity of the slice is 3

Judge whether it is empty

In the chapter of creating variables, it is mentioned that if a variable is not given an initialization value when it is created, a nil value will be assigned by default during compilation. Therefore, a slice is judged to be empty and directly compared with nil.

// Judge empty slice
slice3 := make([]string, 2, 3)
if slice3 == nil {
 fmt.Println("slice3 It's an empty slice")
} else {
 fmt.Println("slice3 Not an empty slice")
}

var slice4 []string
if slice4 == nil {
 fmt.Println("slice4 It's an empty slice")
} else {
 fmt.Println("slice4 Not an empty slice")
}
// output
slice3 Not an empty slice
slice4 It's an empty slice
// Error presentation
slice1 := make([]int, 2, 5)
slice2 := make([]int, 2, 5)
if slice1 == slice2 {
 fmt.Println("equal")
} else {
 fmt.Println("Don't want to wait")
}
// output
slice1 == slice2 (slice can only be compared to nil)

When using make to create a slice, a slice with a length of 2 and a capacity of 3 is defined. Although the slice content is [], it actually has a value, just a null value. Slice is a dynamic structure, which can only be determined to be equal to nil, and cannot be determined to be equal to each other. After declaring a new slice, you can use the append() function to add elements to the slice.

Slice append

Additional definition

Use append() to dynamically add elements to the start position, end position or middle position of the slice.

Syntax format

append(slice, element)

1.slice, the slice to be added must be a slice.

2.element, the element added to the slice, which can be a single element, multiple elements or slices.

Tail append

// Append element at the start of slice
var slice []int = []int {1,2,3}

// Prints the length and capacity of the original slice
fmt.Println("The original slice length and capacity are", len(slice), cap(slice))

// Append an element to the slice
slice = append(slice, 1)
fmt.Println(slice)

// Append multiple elements to the back of the slice
slice = append(slice, 6,7,8,9)
fmt.Println(slice)

// Print the length and capacity of the new slice
fmt.Println("The new slice length and capacity are", len(slice), cap(slice))
// The printed contents are as follows:
The original slice length and capacity were 3
[1 2 3 1]
[1 2 3 1 6 7 8 9]
The length and capacity of the new slice were 8 and 12, respectively

matters needing attention

1. Add elements at the end of the slice, which can only be a single element or multiple elements separated by ",", but not other data types.

2. If the capacity is insufficient when adding elements to the slice, the slice will expand automatically. The rule of automatic capacity expansion is a multiple of 2. The following code:

// Verify automatic expansion of additional elements of slice
var numbers []int
for i := 0; i < 10; i++ {
    numbers = append(numbers, i)
    fmt.Printf("len: %d  cap: %d pointer: %p\n", len(numbers), cap(numbers), numbers)
}
// output
len: 1  cap: 1 pointer: 0xc0420080e8
len: 2  cap: 2 pointer: 0xc042008150
len: 3  cap: 4 pointer: 0xc04200e320
len: 4  cap: 4 pointer: 0xc04200e320
len: 5  cap: 8 pointer: 0xc04200c200
len: 6  cap: 8 pointer: 0xc04200c200
len: 7  cap: 8 pointer: 0xc04200c200
len: 8  cap: 8 pointer: 0xc04200c200
len: 9  cap: 16 pointer: 0xc042074000
len: 10  cap: 16 pointer: 0xc042074000

Start position append

// Appends an element to the start of the slice
var slice = []int {1,2,3}
slice = append([]int {0}, slice...) // Add a slice with only 1 element at the beginning
slice = append([]int {-3,-2,-1}, slice...) // Add a slice with multiple elements at the beginning
fmt.Println(slice)
// output
[-3 -2 -1 0 1 2 3]

1. Add an element at the beginning of the slice. Take the added element as the first parameter of append(), and the second parameter is the original slice. You need to add "..." after the original slice.

2. The first parameter of append() must be slice.

3. Adding elements at the beginning of the slice will generally lead to memory reallocation, and all existing elements will be copied once. Therefore, the performance of adding elements from the beginning of the slice is much worse than that of adding elements from the tail.

Middle position append

// Append an element to the middle of the slice
var slice2 = []int {1,2,3,7,8,9}
slice2 = append(slice2[0:3], append([]int {4,5,6},slice2[3:]...)...)
fmt.Println(slice2)
// output
[1 2 3 4 5 6 7 8 9]

1. Append elements to the middle of the slice. The basic format is append(a[:i], append([]int{x}, a[i:]...)...) / / insert x at the ith position

2. The second append call in each add operation will create a temporary slice, copy the contents of a[i:] to the newly created slice, and then append the temporarily created slice to a[:i].

copy

Definition of replication

The language's built-in function, copy(), can copy one array slice into another array slice. If the two array slices added are not the same size, they will be copied according to the number of elements of the smaller array slice.

copy( destSlice, srcSlice []T) int

Where srcSlice is the data source slice and destSlice is the copy target (that is, copy srcSlice to destSlice). The target slice must be allocated enough space to carry the number of copied elements, and the data types of the source and target must be consistent. The return value of copy() function represents the number of elements actually copied.

Sample code

slice1 := []int{1, 2, 3, 4, 5}
slice2 := []int{5, 4, 3}
copy(slice2, slice1) // Only the first three elements of slice1 will be copied to slice2
fmt.Println(slice2)
// output
[1 2 3]
slice1 := []int{1, 2, 3, 4, 5}
slice2 := []int{5, 4, 3}
copy(slice1, slice2) // Only the three elements of slice2 will be copied to the first three positions of slice1
fmt.Println(slice1)
// output
[5 4 3 4 5]

Although it is more direct to copy slice elements by loop, the built-in copy() function is more convenient to use. The first parameter of copy() function is the target slice to be copied, and the second parameter is the source slice. Two slices can share the same underlying array, and there is no problem even if there is overlap. The example code is as follows:

// Demonstrate slices by looping
slice1 := make([]int, 2, 110)
slice2 := make([]int, 2, 110)
slice1[0] = 1
slice1[1] = 2
slice2[0] = 5
fmt.Println(slice1)// [1 2]
fmt.Println(slice2)// [5 0]


// Copy slice 1 to slice 2
for i := 0; i < 2; i++ {
  slice2[i] = slice1[i]
}
fmt.Println(slice2)// [1 2]
// Copy slice 2 to slice 1
for i := 0; i < 2; i++ {
  slice1[i] = slice2[i]
}
fmt.Println(slice1)// [5 0]

Reference and copy

package main
import "fmt"
func main() {
    // Set the number of elements to 1000
    const elementCount = 1000
    // Pre allocate enough element slices
    srcData := make([]int, elementCount)
    // Assign slice value
    for i := 0; i < elementCount; i++ {
        srcData[i] = i
    }
    // Reference slice data
    refData := srcData
    // Pre allocate enough element slices
    copyData := make([]int, elementCount)
    // Copy data to a new slice space
    copy(copyData, srcData)
    // Modify the first element of the original data
    srcData[0] = 999
    // Prints the first element of the reference slice
    fmt.Println(refData[0])
    // Prints the first and last elements of the duplicate slice
    fmt.Println(copyData[0], copyData[elementCount-1])
    // Copy original data from 4 to 6 (not included)
    copy(copyData, srcData[4:6])
    for i := 0; i < 5; i++ {
        fmt.Printf("%d ", copyData[i])
    }
}

Execute line 8 of the logic and define that the total number of elements is 1000. In line 11, an integer slice with 1000 elements is pre allocated, which will be used as the original data. In lines 14 to 16, fill srcData with integer values from 0 to 999. In line 19, reference refData to srcData, and the slice will not copy elements because of the equal sign operation. In line 22, pre allocate the same type of slice copyData of the same size as srcData. Line 24, use the copy() function to copy the original data into the copyData slice space. In line 27, modify the first element of the original data to 999. Line 30, the first element of the reference data will change. Line 33, print the first data of the copied data. Since the data is copied, it will not change. Line 36, copy the local data of srcData to copyData. Lines 38 to 40 print the copyData element after copying the local data.

The copy of slices is another allocation in memory to allocate the allocated space to the target space. If the original space changes, the newly allocated space will not be affected. References to slices are affected.

Slice deletion

The slice itself does not have a delete function operation. You can only use the properties of the slice itself. There are three situations for deleting slices: deleting the beginning, deleting the end and deleting the middle.

Delete beginning

// Delete slice start element
// 1. Using slice interception method
slice = []int{1, 2, 3}
slice = slice[1:] // Delete the first element
slice = slice[N:] // Delete the first N elements

// 2. Use the append() function in the slice
slice = []int{1, 2, 3}
slice = append(slice[:0], slice[1:]...) // Delete the first element
slice = append(slice[:0], slice[N:]...) // Delete the first N elements

// 3. Use the copy() function of the slice
slice = []int{1, 2, 3}
slice = slice[:copy(slice, slice[1:])] // Delete the first element
slice = slice[:copy(slice, slice[N:])] // Delete the first N elements

Using the append() function, the data pointer is not moved, but the subsequent data is moved to the beginning. It can be completed in place with append (the so-called in place completion refers to the completion within the memory interval corresponding to the original slice data, which will not lead to the change of memory space structure).

Delete middle

// Delete middle
slice = []int{1, 2, 3, ...}
slice = append(slice[:i], slice[i+1:]...) // Delete the middle 1 element
slice = append(slice[:i], slice[i+N:]...) // Delete the middle N elements
slice = slice[:i+copy(slice[i:], slice[i+1:])] // Delete the middle 1 element
slice = slice[:i+copy(slice[i:], slice[i+N:])] // Delete the middle N elements

Delete end

// Delete end
slice = []int{1, 2, 3}
slice = slice[:len(slice)-1] // Delete the last 1 element
slice = slice[:len(slice)-N] // Delete N tail elements

Specify location

// Deletes the specified location of the slice
 seq := []string{"a", "b", "c", "d", "e"}
// Specify delete location
index := 2
// View the elements before and after the deletion position
fmt.Println(seq[:index], seq[index+1:])
// Connect the elements before and after the deletion point
seq = append(seq[:index], seq[index+1:]...)
fmt.Println(seq)

sort

// Integer sort
sli := []int{1, 5, 3, 4}
sort.Ints(sli)
for index, value := range sli {
fmt.Println(index, value)
}
fmt.Println("---------------")
// String sorting
sliStr :=[]string{"lisi", "zhangsan", "bruce"}
sort.Strings(sliStr)
fmt.Println(sliStr)
fmt.Println("---------------")
// Floating point sort
sliFloat := []float64{12.56, 12.12}
sort.Float64s(sliFloat)
fmt.Println(sliFloat)
// output
0 1
1 3
2 4
3 5
---------------
[bruce lisi zhangsan]
---------------
[12.12 12.56]

iterator

Go language has a special keyword range, which can iterate each element in the slice with the keyword for, as shown below:

// Create an integer slice and assign a value
slice := []int{10, 20, 30, 40}
// Iterates over each element and displays its value
for index, value := range slice {
    fmt.Printf("Index: %d Value: %d\n", index, value)
}
// output
Index: 0 Value: 10
Index: 1 Value: 20
Index: 2 Value: 30
Index: 3 Value: 40

Posted by abazoskib on Thu, 25 Nov 2021 21:24:58 -0800