Go language learning 5-slice type

3. Go language data types

In the previous article, we introduced the array type of the Go language, and this article will introduce the slice type of the Go language.

3.3 Slicing

Slicing can be seen as a form of packaging for arrays. The array wrapped by a slice is called the underlying array of the slice. A slice is a descriptor for a continuous segment in its underlying array.

3.3.1 Type notation

For a slice type whose element type is T, its type literal is:

[]T

It can be seen that the length is not part of the slice type (that is, it does not appear in the type literal that represents the slice type). In addition, the length of the slice is variable. Slice values ​​of the same type may have different lengths.

The element type in the slice type declaration can also be any valid Go language data type. E.g:

[]rune

The above is used to indicate the slice type whose element type is rune.

You can also use an anonymous structure type as the element type of the slice type. E.g:

[] struct {name, department string}

3.3.2 Value notation

Similar to an array, it is also a type of compound literal, for example:

[]string{"Go", "Python", "Java", "C", "C++", "PHP"}

There is no stipulation about the length in the type to which the slice value belongs. The following slices are legal:

[]string{8: "Go", 2: "Swift", "Java", "C", "C++", "PHP"}

The above is equivalent to the following compound literal:

[]string{0: "", 1: "", 2: "Swift", 3: "Java", 4: "C", 5: "C++", 6: "PHP", 7: "", 8: "Go"}

3.3.3 Properties and basic operations

The zero value of the slice type is nil. Before initialization, the value of a slice type variable is nil .

Although there is no statement about the length in the slice type, the value has a length, which is reflected in the actual number of element values ​​that they contain. You can use the built-in function len to get the length of the slice value. E.g:

len([]string{8: "Go", 2: "Swift", "Java", "C", "C++", "PHP"})

The result of the above calculation is 9. This slice value actually contains 6 explicitly specified string type values ​​and 3 filled string type zero values ​​"".

Note : Applying the built-in function len to the zero value (ie nil ) of the slice type will result in 0.

The underlying implementation of the slice value:
A slice value always holds a reference to an array value. Once a slice value is initialized, it will be associated with an array value that contains the element values. This array value is called the underlying array that references his slice value.

Multiple slice values ​​may share the same underlying array. For example, if one slice value is copied into multiple ones, or a new slice value is being sliced ​​for a certain continuous segment, these slice values ​​will all refer to the same underlying array. The modification of the element value in the slice value is essentially the modification of the corresponding element on the underlying array. Conversely speaking, changes to the element value in the array value as the underlying element will also be reflected in all slice values ​​that refer to the underlying array and contain the element value.

In addition to length, the slice value has a very important attribute-capacity. The capacity of the slice value is related to the length of the underlying array it holds. It can be obtained through the built-in function cap. E.g:

cap([]string{8: "Go", 2: "Swift", "Java", "C", "C++", "PHP"})

The capacity of the slice value is 9, which is equal to its length. This is a special case, but in many cases this is not the case, let's listen slowly.

The underlying data structure of the slice value:
The underlying data structure of a slice value contains a pointer type value pointing to the underlying array, an int type value representing the length of the slice, and an int type value representing the capacity of the slice.
Write picture description here

You can use a slice expression to "cut" a continuous segment from an array value or slice value, and generate a new slice value. E.g:

array1 := [...]string{8: "Go", 2: "Swift", "Java", "C", "C++", "PHP"}
slice1 := array1[:4]

The underlying array of the value of the variable slice1 is actually the value of the variable array1, as shown below:

Write picture description here

After the above description, you may think that the capacity of a slice may be the length of its underlying array. But this is not the case. Here we create another slice value. E.g:

slice2 := array1[3:]

The underlying array of the value of the variable slice2 is also the value of the variable array1, as shown below:

Write picture description here

As shown above, the capacity of the value of slice2 is not equal to the length of the value of array1. In fact, the capacity of a slice value is the count value from the element value pointed to by the pointer to the last element value of the underlying array . The meaning of the capacity of the slice value is the maximum number of element values ​​in the current underlying array that it can access.

The slice value can be expanded to view more underlying array elements. However, the window cannot be expanded directly by re-slicing. For example, perform the following operations on the original slice1 value above:

slice1[4]

This will cause a runtime panic, because the index value exceeds the current length of the slice value, which is not allowed. The correct way to expand is as follows:

slice1 = slice1[:cap(slice1)]

By re-slicing slice1 to the maximum, you can see the most bottom-level array element values. At this time, the length of the value of slice1 is equal to its capacity.

Note: The capacity of a slice value is fixed. In other words, the maximum number of underlying array elements that can be seen is fixed.

The slice value cannot be expanded beyond its capacity, for example:

slice1 = slice1[:cap(slice1)+1]//超出slice1容量的范围,这样会引起一个运行时恐慌

A slice value can only expand in the direction of increasing index. E.g:

slice2 = slice2[-2:] //这会引起一个运行时恐慌。另外,切片值不允许由负整数字面量代表。

Use the append function to extend the value of slice1 at the beginning:

slice1 = append(slice1, "Ruby", "Erlang")

After executing this statement, the value of the slice type variable slice1 and the state of its underlying array (the value of the array variable array1) are as follows:

Write picture description here

It can be seen that the length of the value of slice1 has increased from the original 4 to 6, which is the same as its capacity. But since the length of this value has not exceeded its capacity, there is no need to create a new underlying array.

The original slice1 value is:

[]string{"Go", "Python", "Java", "C"}

The current value of slice1 is:

[]string{"Go", "Python", "Java", "C", "Ruby", "Erlang"}

The original value of array1 is:

[6]string{"Go", "Python", "Java", "C", "C++", "PHP"}

The current value of array1 is:

[6]string{"Go", "Python", "Java", "C", "Ruby", "Erlang"}

Extend the current slice1 as follows:

slice1 = append(slice1, "Lisp")

After executing this statement, the length of the value of the variable slice1 exceeds its capacity. At this time, a new array value will be created and initialized. This new array value will be used as the bottom array of the slice value newly created in the append function, and it will contain all the element values ​​in the original slice value and all the element values ​​as the extended content. The pointer in the new slice value will point to the first element value of its underlying array, and its length and capacity are the same as the length of its underlying array. Finally, this new slice value will be assigned to the variable slice1.

You can use the append function to connect two slice values ​​with the same element type. E.g:

slice1 = append(slice1, slice...)

Of course, you can also pass the array value as the second parameter to the append function.

Even if the value of the slice type variable is zero nil, it will be regarded as a slice value with a length of 0. E.g:

slice2 = nil
slice2 = append(slice2, slice1...)

Or as follows:

var slice4 []string
slice4 = append(slice4, slice...)

The first statement above is used to declare (not include initialization) a variable. Start with the keyword var , followed by the name and type of the variable. The value of the uninitialized slice variable is nil .

3.3.4 Complex usage of slices

Add a third index to the slicing expression-----capacity upper bound index. If specified, the capacity of the slice value of the evaluation result of the slice expression is no longer the difference between the capacity of the operation object of the slice expression and the lower bound index of the element in the expression, but the capacity The difference between the bound index and the lower bound index of the element.

The purpose of specifying the upper bound index of the capacity is to reduce the capacity of the new slice value, which allows a more flexible data isolation strategy.

var array2 [10]int = [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
slice5 := array2[2:6]

As above, we can directly access and modify the element value in array2 whose index value is within the range of [2,6).

slice5 = slice5[:cap(slice5)]

After slicing as above, you can access and modify the value of the element whose index value is in the range of [2,10) in the value of array2.

If the value of slice5 is passed to another program as a data carrier, then this program can change the value of some elements in the value of array2 at will. This is equivalent to exposing part of the implementation details of the program and revealing a method that can indirectly modify the internal state of the program, which is often not what we want.

If slice5 is declared like this:

slice5 := array2[2:6:8]

In this way, the holder of slice5 can only access and modify the value of the element whose index value is within the range of [2,8) in the value of array2.

slice5 = slice5[:cap(slice5)]

Even if slice5 is expanded to the maximum, those elements whose corresponding index value is greater than or equal to 8 in the value of array2 cannot be accessed through it. At this time, the capacity of the value of slice5 is 6 (the difference between the upper bound index of the capacity and the lower bound index of the element). For slicing operations, the capacity of the operated object is an insurmountable limit. The value of slice5 restricts the "access rights" of its underlying array (value of array2).

If the expansion above the value of slice5 exceeds its capacity:

slice5 = append(slice5, []int{10, 11, 12, 13, 14, 15}…)

Then its original underlying array will be replaced. It also completely cuts off the way to access and modify the element values ​​in the original underlying array through slice5.

Limitation of the 3 indexes in the slice expression: When the upper bound index of the capacity is specified in the slice expression, the upper bound index of the element cannot be omitted. However, in this case, the lower bound index of the element can be omitted. E.g:

slice5[:3:5]//合法的切片表达式
slice5[0::5]//非法的切片表达式,会造成一个编译错误

Batch copy the elements in the slice value

sliceA := []string{"Notepad", "UltraEdit", "Eclipse"}
sliceB := []string{"Vim", "Emacs", "LiteIDE", "IDEA"}

Use the built-in function copy of the Go language to copy the elements in the value of the variable sliceB to the value of sliceA. E.g:

n1 := copy(sliceA,sliceB)

The function of the built-in function copy is to copy the element value in the source slice value (the second parameter value) to the target slice value (the first parameter value), and return the number of element values ​​copied. The element types of the two parameters of the copy function must be the same, and the number of element values ​​it actually copies will be equal to the length of the shorter slice value.

The value of the variable n1 is 3, and the value of the variable sliceA is modified to:

[]string{"Vim", "Emacs", "LiteIDE"}

Guess you like

Origin blog.csdn.net/u012855229/article/details/115367385