Golang learning road-an article to take you to understand slice (slice)

Basic introduction to slicing

The slice itself is not a dynamic array or array pointer. Its internal data structure refers to the underlying array through pointers, and sets related attributes to limit data read and write operations to a designated area. The slice itself is a read-only object, and its working mechanism is similar to an encapsulation of an array pointer.

A slice is a reference to a continuous segment of an array, so a slice is a reference type (thus more similar to the array type in C/C++, or the list type in Python). This segment can be the entire array, or a subset of items identified by the start and end indexes. It should be noted that the item identified by the termination index is not included in the slice. Slicing provides a dynamic window with a pointer to the array.

The slice index of a given item may be smaller than the index of the same element of the related array. Unlike the array, the length of the slice can be modified at runtime, the minimum is 0 and the maximum is the length of the associated array: a slice is an array with variable length.

The basic syntax of slice definition:

var 切片名[]类型
如:var a[]int

Quick start for slicing

package main

import(
	"fmt"
)

func main(){
    
    

	var arr [5]int = [...]int{
    
    5,4,3,2,1}

	//定义一个切片
	//引用arr数组的起始下标为1,最后的下标为3(但不包括3)
	slice := arr[1:3]
	fmt.Println("arr=",arr)
	fmt.Println("slice 的元素是 =",slice)
	fmt.Println("slice 的元素个数 =",len(slice))
	fmt.Println("slice 的容量 =",cap(slice))//切片的容量是可以动态变化的

}

operation result:
Insert picture description here

Slice in memory form

Memory layout of slices:
Insert picture description here
description:

  • slice is a reference type
  • From the bottom, slice is actually a data structure (struct structure)
type slice struct{
    
    
   ptr *[2] int
   len int
   cap int
}

nil slice and empty slice

Nil slices and empty slices are also commonly used.

nil slice

var slice []int

Insert picture description here
Nil slices are used in many standard libraries and built-in functions. When describing a slice that does not exist, nil slices are needed. For example, when an exception occurs in a function, the returned slice is a nil slice. The nil slice pointer points to nil.

Empty slice

Empty slices are generally used to represent an empty collection. For example, if a database query is not found, an empty slice can be returned.

slice := make([]int, 0)
slice := []int{
    
    }

Insert picture description here
The difference between an empty slice and a nil slice is that the address pointed to by the empty slice is not nil, it points to a memory address, but it does not allocate any memory space, that is, the underlying element contains 0 elements.

The last point that needs to be explained is. Regardless of whether nil slices or empty slices are used, the effects of calling the built-in function append, len and cap are the same.

Use of slices

Way 1

Define a slice, and then let the slice refer to an array that has been created, as in the previous case.

func main(){
    
    

	var arr [5]int = [...]int{
    
    5,4,3,2,1}

	//定义一个切片
	//引用arr数组的起始下标为1,最后的下标为3(但不包括3)
	slice := arr[1:3]
	fmt.Println("arr=",arr)
	fmt.Println("slice 的元素是 =",slice)
	fmt.Println("slice 的元素个数 =",len(slice))
	fmt.Println("slice 的容量 =",cap(slice))//切片的容量是可以动态变化的
}

Way 2

Use make to create slices.

Basic syntax:

var 切片名 []type = make([]type,len,[cap])

Parameter Description:

  • type is the data type
  • len: size
  • cap: Specify the slice capacity, optional, if allocated, cap>=len is required

Case presentation

package main

import(
	"fmt"
)

func main(){
    
    

   var slice []int = make([]int ,5,10)
   slice[1] = 10
   slice[2] = 20
   fmt.Println(slice)
   fmt.Println("slice的size =",len(slice))
   fmt.Println("slice的cap =",cap(slice))
}

Operation result:
Insert picture description here
Description:

  • The size and capacity of the slice can be specified by creating a slice by make
  • If no value is assigned to each element of the slice, then the default value is used.
  • The array corresponding to the slice created by the make method is maintained by the bottom layer of make and is invisible to the outside world, that is, each element can only be accessed through the slice.

Way 3

Define a slice, directly specify the specific array, the principle of use is similar to the way of make.

Case:

package main

import(
	"fmt"
)

func main(){
    
    

   var slice []string = []string{
    
    "Casey","Lily","Sally"}
   fmt.Println(slice)
   fmt.Println("slice的size =",len(slice))
   fmt.Println("slice的cap =",cap(slice))
}

operation result:
Insert picture description here

The difference between method 1 and method 2 (interview)

  • Method 1 is to directly reference the array, this array is pre-existing and visible to the programmer.
  • Method 2 is to create a slice through make, and make will also create an array, which is maintained by the slice at the bottom level, which is invisible to programmers.
    Schematic diagram of make creating slices:
    Insert picture description here

Slice traversal

The traversal of slices is the same as that of arrays. There are two ways to traverse the for loop and the for-range structure to traverse the slices.
Case demonstration:

package main

import(
	"fmt"
)

func main(){
    
    

  var slice []string = []string{
    
    "Casey","Lily","Sally"}
  //常规for循环遍历切片
  for i := 0; i < len(slice); i++{
    
    
	  fmt.Printf("slice[%d] = %v\n", i, slice[i])
  }

  //使用for-range结构遍历切片

  for index, value := range slice{
    
    
	  fmt.Printf("i = %v, v = %v\n", index, value)
  }

}

operation result:
Insert picture description here

The append built-in function dynamically appends slices

Case presentation

package main

import(
	"fmt"
)

func main(){
    
    

  var slice []string = []string{
    
    "Casey","Lily","Sally"}
  //通过append内置函数,可以对切片进行动态追加 
  slice = append(slice,"Jerry","Tom")
  fmt.Println("slice =",slice)

  //通过append将切片slice追加给slice
  slice = append(slice,slice...)
  fmt.Println("slice =",slice)

}

operation result:
Insert picture description here

Underlying analysis

Schematic:
Insert picture description here
Description:

  • The essence of the slice append operation is to expand the array
  • The bottom layer of go will create a new array newArr (size after installation and expansion)
  • The original elements of the slice are copied to the new array newArr, and the slice is re-referenced to newArr.

Slice copy operation

The slice uses the copy built-in function to complete the copy.
Case:

package main

import(
	"fmt"
)

func main(){
    
    

  var slice []string = []string{
    
    "Casey","Lily","Sally"}
  var slice1 = make([]string,5)
  copy(slice1,slice)
  fmt.Printf("slice = %v\n",slice)
  fmt.Printf("slice1 = %v\n", slice1)

  var slice2 []int = []int{
    
    10,20,30}
  var slice3 = make([]int,5)
  copy(slice3,slice2)
  fmt.Printf("slice2 = %v\n",slice2)
  fmt.Printf("slice3 = %v\n", slice3)

}

Operation result:
Insert picture description here
Description:

  • The data type of the copy(para1,para2) parameter is slice.
  • According to the above code: the data space of slice and slice, slice2 and slice3 are independent and do not affect each other.

Precautions for the use of slices

  • When the slice is initialized, var slice = arr[startIndex:endIndex]
    Description: From the arr array subscript as startIndex, get the element with the subscript as endIndex (not including endIndex)
  • When the slice is initialized, it still cannot cross the boundary, the range is 0 ~ len(arr)-1, but it can grow dynamically.
var slice = arr[0:end] 可简写 var slice = arr[:end]
var slice = arr[start:len(arr)]可简写var slice = arr[start:]
var slice = arr[0:len(str)] 可简写 var slice = arr[:]
  • cap is a built-in function used to count the capacity of a slice, that is, how many elements can be stored at most.
  • After the slice is defined, it cannot be used because it is empty and needs to be referenced to an array or make a space for the slice to use.
  • Slicing can continue slicing.
    Case:
package main

import(
	"fmt"
)

func main(){
    
    

  var slice []string = []string{
    
    "Casey","Lily","Sally"}
  slice1 := slice[0:2]
  //slice 和 slice1指向的数据空间是同一个,因此slice[1] = "abc"
  slice1[1] = "abc"

  fmt.Println("slice =",slice)
  fmt.Println("slice1 =",slice1)

}

operation result:
Insert picture description here

  • The slice is a reference type , and when it is passed, it obeys the reference pass mechanism.

Guess you like

Origin blog.csdn.net/weixin_44736475/article/details/113999771