In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-03-29 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)05/31 Report--
This article editor for you to introduce in detail "Go slice slice case analysis", the content is detailed, the steps are clear, the details are handled properly, I hope this "Go slice slice case analysis" article can help you solve your doubts, following the editor's ideas slowly in depth, let's learn new knowledge.
Slice stands for slicing (slicing), such as slicing an array to take out some of the values in the array. In modern programming languages, slice (slicing) has almost become a necessary feature. It can extract subarrays (lists) of any length from an array (list), which brings great convenience for the operation of data structures, such as python, perl and so on. Slice operations of arrays are supported, and even perl supports slice of hash data structures.
But slice in Go is quite different from slice in these languages. In the languages mentioned above, slice is a slicing operation that returns a new data object. Slice in Go is not only a slicing action, but also a data structure (just like an array).
Storage structure of slice
Slice in Go depends on arrays, and its underlying layer is arrays, so slice has all the advantages of arrays. And slice support can add elements to slice through append, and dynamically expand when the length is not enough. By slice slices again, you can get a smaller slice structure, which can be iterated, traversed, and so on.
Slice is actually structured like this: first create an underlying array with a specific length and data type, then select some elements from this underlying array, return a collection (or container) of these elements, and point the slice to the first element in the collection. In other words, slice itself maintains a pointer property that points to a collection of elements in its underlying array.
For example, initialize a slice data structure:
My_slice: = make ([] int, 3je 5) / / output slicefmt.Println (my_slice) / / output: [0 000]
This means declaring an underlying array of length 5 and data type int, and then taking three elements from the underlying array (that is, index from 0 to 2) as the structure of the slice.
As shown below:
Each slice structure consists of three parts: capacity (capacity), length (length), and a pointer to an element of the underlying array, each occupying 8 bytes (1 machine word length, a machine word length of 64bit on a 64-bit machine, a total of 8 bytes, and a 32-bit architecture is 32bit, occupying 4 bytes), so any slice is 24 bytes (3 machine words).
Pointer: indicates which element of the underlying array the slice structure starts with, and the pointer points to that element
Capacity: the length of the underlying array, indicating that the slice can be extended to this length at most
Length: indicates the current length of slice. If an element is appended, it will be extended when the length is not enough, and the maximum will be extended to the length of Capacity (not completely accurate, explained when the array expands automatically later), so Length must not be larger than Capacity, otherwise an error will be reported.
For the slice created above, it has a length of 3, a capacity of 5, and a pointer to the index=0 of the underlying array.
You can get the length of the slice through the len () function and the Capacity of the slice through the cap () function.
My_slice: = make ([] int,3,5) fmt.Println (len (my_slice)) / / 3fmt.Println (cap (my_slice)) / / 5
You can also output slice directly through the print () or println () function, which will get the property values of the slice structure, namely length, capacity, and pointer:
My_slice: = make ([] int,3,5) println (my_slice) / / [3x5] 0xc42003df10
[3prime 5] indicates that length and capacity,0xc42003df10 represent pointers to the underlying array elements.
It's important to remember that the essence of slice is [x _ 0xADDR], and that it will help you understand the features of slice in many ways. In addition, I suggest that although slice is not a pointer in nature, it can still be thought of as an impure pointer that contains the other two attributes, that is, it is directly thought of as a pointer. In fact, this is not only true for slice, but also for map.
Create, initialize, and access slice
There are several ways to create slice data structures.
One is to use make ():
/ / create a sliceslice with length and capacity equal to 5: = make ([] int,5) / / length=3,capacity=5 's sliceslice: = make ([] int,3,5)
Make () has a little more operation than the new () function, which only allocates memory and initializes the default assignment of 0, while make () allocates memory for the underlying array, and then generates an additional slice from the underlying array and initializes it. In addition, make can only build data objects of slice, map and channel, because they all point to the underlying data structure and need to allocate memory and initialize the underlying data structure first.
You can also create a slice by directly assigning values and initializing them:
/ / create a slice with a length and capacity of 4, and initialize the assignment color_slice: = [] string {"red", "blue", "black", "green"} / / create a slice with a length and capacity of 100, and assign the 100th element 3slice: = [] int {99:3}
Note the distinction between array and slice:
/ / create an int array of length 3 array: = [3] int {10,20,30} / / create a sliceslice of length and capacity of 3: = [] int {10,20,30}
Because the underlying slice is an array, you can use an index to access slice or modify the value of an element in slice:
/ / create a slicemy_slice with a length of 5 and a capacity of 5: = [] int {11, 22, 33, 44, 55} / / access the second element of slice, print (my_slice [1]) / / modify the value of the third element of slice, my_slice [2] = 333
Because the underlying layer of slice is an array, accessing my_slice [1] is actually accessing the corresponding elements of its underlying array. The only elements that can be accessed by slice are those within the scope of length. Those elements outside the length but within the capacity are not yet part of the slice. Only when the slice is extended (see append below) will the elements in the capacity be included in the length and can be accessed.
Nil slice and empty slice
When a slice is declared but not initialized, the slice is a nil slice.
/ / declare a nil slicevar nil_slice [] int
Nil slice indicates that its pointer is nil, which means that the slice does not point to any underlying array. As a result, the length and capacity of nil slice are both 0.
| |-| nil | 0 | 0 | | ptr | Length | Capacity | |-|
You can also create an empty slice (Empty Slice), where an empty slice represents a length of 0 and a capacity of 0, but has a pointing slice, but the underlying array is temporarily an empty array of length 0.
/ / use make to create empty_slice: = make ([] int,0) / / directly create empty_slice: = [] int {}
The structure of Empty Slice is as follows:
| |-| ADDR | 0 | 0 | | ptr | Length | Capacity | |-|
Although nil slice and Empty slice have a length and capacity of 0, output results of [], and do not store any data, they are different. Nil slice does not point to the underlying array, while an empty slice points to the underlying array, but the underlying array is temporarily empty.
You can use println () to output validation:
Package mainfunc main () {var nil_s [] int empty_s:= [] int {} println (nil_s) println (empty_s)}
Output result:
[0/0] 0x0 [0/0] 0xc042085f50
Of course, both nil slice and empty slice can be manipulated, such as the append () function, the len () function, and the cap () function.
Slice the slice
You can continue slicing from slice to generate a new slice, which reduces slice.
The syntax for slice slices is:
SLICE[A: B] SLICE[A: B:C]
Where A means to cut from the first element of SLICE, B controls the length of the slice (BMel A), and C controls the capacity of the slice (C Mel A). If no C is given, it means cutting to the end of the underlying array.
There are also several simplified forms:
SLICE [A:] / / cut from A to the end of SLICE [: B] / / from the beginning to B (excluding B) SLICE [:] / / cut from beginning to end, which is equivalent to copying the entire SLICE
For example:
My_slice: = [] int {11Power22 int 33meme44Power55} / / generate a new slice, take it from the second element, and cut the length as 2new_slice: = my_slice [1:3]
Note that when intercepting, "close left and open right". So the above new_slice is intercepted from the index=1 of my_slice to index=3, but does not include the element index=3. Therefore, the new slice is a new data structure consisting of the second element and the third element in my_slice, with a length of 2.
The following is the structure of the slice slice after the new slice is generated:
It is not difficult to find that an underlying array can generate countless slice, and for new_slice, it does not know that element of the underlying array index=0.
You can also control the capacity of the new slice when slicing:
My_slice: = [] int {11Power22, 33author44, 55} / / from the second element, the length cut is 2, and the capacity is also 2new_slice: = my_ slice [1: 3:3]
At this time, the length of the new slice is equal to capacity, and the index=4 and 5 of the underlying array will never be visible to new_slice, even if append () in the face of new_slice leads to the expansion of the underlying array. See below for details.
When there is a lot of slice in the same underlying array, everything becomes messy, because it is impossible to remember who is sharing it, and by modifying the elements of a slice, it will also affect the slice that we may not want to affect. Therefore, we need a feature to ensure that the underlying arrays of each slice do not affect each other, as shown in the "expansion" below.
Copy () function
You can copy one slice to another slice.
$go doc builtin copyfunc copy (dst, src [] Type) int
This means that if the src slice is copied to dst slice,src longer than dst, it will be truncated, and if src is shorter than dst, only the part of src will be copied.
The return value of copy is the number of elements that were successfully copied, so it is the smallest length in src slice or dst slice.
For example:
In addition, copy can copy a string into byte slice because the string is actually [] byte.
Func main () {S1: = [] byte ("Hello") num: = copy (S1, "World") fmt.Println (num) fmt.Println (S1) / / output [87 111 114 108 100 32] fmt.Println (string (S1)) / / output "World"} append () function
You can use the append () function to extend slice, because it appends elements to the slice, so it must increase the length of the slice.
It must be noted, however, that the result of append () must be used. The so-called used, can be output, can be assigned to a certain slice. If you put append () in an empty context, you will get an error: append () is evaluated but not used. It also means that append () returns a new slice and the original slice remains unchanged.
For example:
My_slice: = [] int {11Power22 new_slice: = my_slice [1:3] / / append () append an element 2323 to return the new sliceapp_slice: = append (new_slice,2323)
The above append () adds an element 2323 after new_slice, so app_slice [2] = 2323. But because these slice share the same underlying array, 2323 is also reflected in other slice.
The current data structure is as follows:
Of course, if the result of append () is reassigned to new_slice, new_slice increases the length.
Similarly, because the essence of string is [] byte, string can be append into byte slice:
S1: = [] byte ("Hello") S2: = append (S1, "World"...) fmt.Println (string (S2)) / / output: HelloWorld expansion
When the length of slice is equal to capacity, appending elements to slice with append () automatically expands the length of the underlying array.
When the underlying array is extended, a new underlying array is generated. So the old underlying array will still be referenced by the old slice, and the new slice and the old slice will no longer share the same underlying array.
Func main () {my_slice: = [] int {11 my_slice: = [] int: = append (my_slice,66) my_slice [3] = 444 / / modify the old underlying array fmt.Println (my_slice) / / [11 22 33 44 55] fmt.Println (new_slice) / / [11 22 33 44 55 66] fmt.Println (len (my_slice), ":" Cap (my_slice)) / / 5:5 fmt.Println (len (new_slice), ":", cap (new_slice)) / / 6:10}
From the above results, it can be found that the underlying array is expanded to 10, and it is a new underlying array.
In fact, when the underlying array needs to be expanded, it will be expanded by 2 times the length of the current underlying array, and a new array will be generated. If the length of the underlying array exceeds 1000, the capacity will be expanded at a rate of 25%, that is, when there are 1000 elements, it will be expanded to 1250, but the algorithm for this growth rate may change with the evolution of the go version.
In fact, the above statement should be changed: when capacity needs to be expanded, the array will be expanded by twice the current capacity. In other words, how to expand the capacity is judged according to the nature of slice, the capacity y of 0xADDR. The reason for the special emphasis on these two differences is that it is easy to get confused.
For example, when the expanded object is a true subset of the underlying array:
My_slice: = [] int {11Power22 int 33Power44 55} / / limit the length and capacity, and make the length and capacity equal new_slice: = my_ slice [1: 3:3] / / [22 33] / / expand app_slice: = append (new_slice,4444)
The above new_slice has a capacity of 2 and does not correspond to the end of the underlying array, so new_slice is a true subset of my_slice. When you expand the capacity of new_slice, a new underlying array is generated, which has a capacity of 4 instead of 10. As shown below:
Because a new underlying array is created, modifying different slice will not affect each other. In order to make sure that each underlying array is modified each time, a new slice with only one length and only one capacity is usually cut out, so that any expansion of it will generate a new underlying array, thus making each slice's underlying array independent.
My_slice: = [] int {11, 22, 22, 33, 44, 55} new_slice: = my_ slice [2: 3:3] app_slice: = append (new_slice,3333)
In fact, there is still a chance of sharing, because without expansion, the only element is still shared, and modifying it will definitely affect at least 1 slice. Independence can only be guaranteed by cutting out a slice with a length of 0 and a capacity of 0, but this is no different from creating a new slice.
Merge slice
Slice is the same value as an array, and you can merge one slice with another slice to produce a new slice.
When merging slice, simply add... to the second parameter of append (). That's it, that is, append (S1 dint s2...) Signifies merging S2 after S1 and returns a new slice.
S 1: = [] int {1 fmt.Println 2} s 2: = [] int {3pm 4} s 3: = append (s 1 score s 2...) fmt.Println (s 3) / / [1 2 3 4]
Note that append () allows a maximum of two parameters, so you can only merge two slice at a time. However, it can be tricky to use append () as an argument to another append () to achieve multi-level merging. For example, the following merges S1 and S2, and then merges with S3 to get the result of the s1+s2+s3 merge.
Sn: = append (append (s 1 dint s 2...), s 3...) slice ergodic iteration
Slice is a collection, so you can iterate.
The range keyword iterates over slice, returning one index and the corresponding element value at a time. You can combine range iterations with for loops to traverse slice.
Package mainfunc main () {S1: = [] int {11pr 22 3je 44} for index,value: = range S1 {println ("index:", index, "value", value)}}
Output result:
Index: 0, value 11index: 1, value 22index: 2, value 33index: 3, value 44 pass slice to the function
As mentioned earlier, although slice actually contains three attributes and its data structure is similar to that of [3Unip 5] 0xc42003df10, you can still think of slice as a pointer. This feature is directly reflected in the value of function parameters.
The parameters of a function in Go are passed by value, so a copy of the argument is passed to the function when the function is called. If slice is passed to the function, it will make a copy of the slice to the function, which is actually [3x5] 0xc42003df10, so the copy passed to the function still points to the underlying array of the source slice.
In other words, if the slice is modified inside the function, it may directly affect the underlying array outside the function, thus affecting other slice. However, this is not always the case. For example, when the slice is expanded inside the function, a new underlying array is generated. The subsequent code of the function only operates on the new underlying array, so that the original underlying array is not affected.
For example:
Package mainimport "fmt" func main () {S1: = [] int {11,22,33,44} foo (S1) fmt.Println (S1 [1]) / / output: 23} func foo (s [] int) {for index, _: = range s {s [index] + = 1}}
This will output 23, because foo () directly manipulates the original underlying array, adding 1 to each element of the slice.
Slice and memory waste
Because the underlying layer of slice is an array, it is likely that the array is very large, but the number of elements taken by slice is very small, which results in most of the space occupied by the array being wasted.
In particular, the garbage collector (GC) does not collect objects that are being referenced, and when a function returns a slice directly to the underlying array, the underlying array will not be recycled with the function exiting, but will remain forever because of the reference to the slice, unless the returned slice also disappears.
Therefore, when the return value of a function is a data structure that points to the underlying array (such as slice), you should save a copy of the slice inside the function to a new slice that uses your own underlying array and return the new slice. As soon as the function exits, the larger underlying array will be reclaimed, and the small slice will remain in memory.
After reading this, the article "slice instance Analysis of Go slices" has been introduced. If you want to master the knowledge points of this article, you still need to practice and use it yourself to understand it. If you want to know more about related articles, welcome to follow the industry information channel.
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.