Preliminary review
In the previous chapter, we shared the function definition of the GO language, and the simple use of Pointers in the GO language, so in this chapter, the old cat and everyone to learn about containers in the GO language.
An array of
Definition of an array
When it comes to containers, the first thing that comes to mind is arrays. Of course, if you have programming experience, you will think arrays are not containers. But anyway, when it comes to arrays it’s really just a way of storing and organizing data, so don’t get too obsessed with what you call it.
Let’s go straight to the array definition example, as follows:
var arr1 [5]int // Define a default type of length 5
arr2:=[3]int{1.2.3} // Define an array with a length of 3
arr3:=[...]int{1.2.3.4.5.6} // Define an array whose length is computed by the compiler
var grid [4] [5] bool // Define a two-dimensional array with four rows and five columns
fmt.Println(arr1,arr2,arr3,grid)
Copy the code
The above example produces the following output
[0 0 0 0 0] [1 2 3] [1 2 3 4 5 6] [[0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0]]
Copy the code
So just to summarize, arrays actually have a couple of characteristics
- In fact, it is written the opposite of other programming languages, which define the length of the array before the variable type
- The contents stored in an array must be of the same type
Array traversal
So how do we iterate to get the data in the array? In fact, if you have read the previous article of Old Cat, you should know that you can use the for loop to get, and one of the ways that you can easily think of is as follows (let’s take arr3 as an example).
for i:=0; i<len(arr3); i++ { fmt.Println(arr3[i]) }Copy the code
This way, of course, we can get. Next, the old cat actually wants to share with you another way, using the range keyword method
// I represents the subscript of the position of the data in the array, and v represents the actual value
for i,v :=range arr3 {
fmt.Println(i,v)
}
// If we want only value, we can omit I by _
for _,v :=range arr3 {
fmt.Println(v)
}
// If we just want the position subscript, then we can just write down
for i:=range arr3 {
fmt.Println(i)
}
Copy the code
Which one do you think is more elegant? The latter is obvious, meaningful and aesthetically pleasing.
Arrays are passed by value in go
The other thing that’s kind of in sync with you is that arrays are passed as parameters as well as values. Let’s redefine a new function as follows:
func printArray(arr [5]int){
for i,v:=range arr {
println(i,v)
}
}
Copy the code
So let’s make the relevant call in the main function (to illustrate the compilation error, the old cat uses the image here).
In go, even arrays of the same type are of different lengths, but the compiler still considers them to be of different types.
So we can change the value of the array, as shown in the following code
func main(a) {
arr3:=[...]int{1.2.3.4.5} // Define an array of variable length
printArray(arr3)
for i,v:=range arr3 {
println(i,v)
}
}
func printArray(arr [5]int){
arr[0] = 300
for i,v:=range arr {
println(i,v)
}
}
Copy the code
As you can see, the old cat prints twice here. The first time it prints directly in the function, it has changed the first value, and the result printed inside the function is
0 300
1 2
2 3
3 4
4 5
Copy the code
Obviously the inner values have changed, but let’s look at the printed values of the outer functions as follows
0 1
1 2
2 3
3 4
4 5
Copy the code
It doesn’t change, so what does that tell you? What it tells you is that when you call printArray you’re actually passing a copy of the array to the function, the outside array is not being updated, and that tells you that GO is a way of passing values and parameters.
You have to be careful when you’re using this array because you might get poked. You might think that this array is really hard to use, but the good news is that in GO, you don’t actually use arrays directly, you use slicing a lot.
slice
When it comes to slicing, it’s best to understand slicing based on the array above. Let’s start with an example
func main(a) {
arr := [...]int{1.2.3.4.5.6.7}
fmt.Println("arr[2:6]",arr[2:6])
fmt.Println("arr[:6]",arr[:6])
fmt.Println("arr[2:]",arr[2:])
fmt.Println("arr[:]",arr[:])
}
Copy the code
In fact, the definition similar to ‘[]’ is called slice, which refers to a variable length sequence of elements of the same type. Let’s take a look at the results:
arr[2:6] [3 4 5 6]
arr[:6] [1 2 3 4 5 6]
arr[2:] [3 4 5 6 7]
arr[:] [1 2 3 4 5 6 7]
Copy the code
Slice can be viewed as a view. Take ARR [2:6] as an example, we extract elements from the second position to the sixth position on the basis of the original array and re-display them as values. Of course, our values are left-closed and right-open ranges.
Slice is actually a view concept
We have stated that slice is a view of an array, so let’s verify this statement by looking at the following example in detail
func main(a) {
arr := [...]int{1.2.3.4.5.6.7}
fmt.Println("arr[2:6]",arr[2:6])
updateSlice(arr[2:6])
fmt.Println("arr[2:6]",arr[2:6])
fmt.Println(arr)
}
func updateSlice(arr []int){
arr[0] = 100
}
Copy the code
Write a function that updates the value of the first slice position.
In fact, the final execution result is:
arr[2:6] [3 4 5 6]
arr[2:6] [100 4 5 6]
[1 2 100 4 5 6 7]
Copy the code
So why is that? In fact, arR [2:6] is easily understood as 3456, and the second is also easier to understand. When the first value of our slice is updated to 100, we program the second one, so why is the original data also changed to 100? Slice is a view of the original array. When we see that slice has been updated to 100, the underlying data must have changed to 100 as well. (Note that there is nothing to say that a view’s operations do not react on the original array.) Here is still more important, I hope you taste it carefully.
Reslice and extensions
To reslice a slice value of the original slice, let’s look at the following example.
func main(a) {
arr := [...]int{1.2.3.4.5.6.7}
s1 := arr[:]
s2 := s1[2:4]
fmt.Println(s2)
}
Copy the code
In the example above, we can see that s1 is a full slice of the array, and then we slice s1 again. We can easily calculate that the result we get in the second slice is [3,4]. Such behavior is called reslice, which is relatively easy to understand.
Next, let’s deepen the difficulty on this basis. We will resilce again on the basis of S2, as follows:
func main(a) {
arr := [...]int{1.2.3.4.5.6.7}
s1 := arr[:]
s2 := s1[2:4]
s3 := s2[1:3]
fmt.Println(s3)
}
Copy the code
As we all know, the value of s2 is [3,4]. When we reslice it for the next time, we find that it is easier to calculate from the first position to the third position since the value is [1:3]. Based on [3,4], the first position should be 4. What are the results? And I’m going to tell you directly what I’m going to get when I run the old cat
[4 5]
Copy the code
So why is there such a result? Where does the five come from?
Let’s take a look at a diagram arranged under the old cat.
- An array of arr’s with a length of 7 and seven numbers stored in it.
- And then s1 slices it completely, so we get a complete 7.
- It is important to note that at this time, we use a subscript said when s2 for s1 in this section, we found that its essence is to begin the second element of an array of values, as is the concept of view, in fact s2 can also view arr fantasy out of the other two position, is let’s say a gray subscript 3 and 4.
- The same we will s3, thus we s3 is on the basis of s2 section again, in theory, there are three values, respectively is 0, 1, 2 subscript values, but we found that 3 s2, indicating the position of the make-believe, no real existence value and the matching, therefore, let’s take after the intersection and can only take out two array arr, So that’s the final [4,5].
This is an extension to Slice. Let’s take a look at the underlying implementation of slice.
In fact, slice generally contains three concepts. The bottom layer of slice is an empty array structure. PTR is a pointer to the first position of the array, Len represents the available length of a specific slice, and CAP represents the length that can be extended.
We have a function called to get len and cap. Let’s look at the example above and print out the length and extend cap. The printed code is as follows.
func main(a) {
arr := [...]int{1.2.3.4.5.6.7}
s1 := arr[:]
s2 := s1[2:4]
s3 := s2[1:3]
fmt.Printf("arr=%v\n",arr)
fmt.Printf("s1=%v,len(s1)=%d,cap(s1)=%d\n",s1,len(s1),cap(s1))
fmt.Printf("s2=%v,len(s2)=%d,cap(s2)=%d\n",s2,len(s2),cap(s2))
fmt.Printf("s3=%v,len(s3)=%d,cap(s3)=%d\n",s3,len(s3),cap(s3))
}
Copy the code
The output of the above code is
arr=[1 2 3 4 5 6 7]
s1=[1 2 3 4 5 6 7].len(s1)=7.cap(s1)=7
s2=[3 4].len(s2)=2.cap(s2)=5
s3=[4 5].len(s3)=2.cap(s3)=4
Copy the code
When our value exceeds cap, an error will be reported. For example, s2 is S2 :=[2:4], and now we find that cap is 5. If we exceed 5, s2 can be written as S2 :=[2:8], and the following exception will be reported
panic: runtime error: slice bounds out of range [:8] with capacity 7
goroutine 1 [running]:
main.main()
E:/project/godemo/part6-slice.go:8 +0x7f
Copy the code
And if we take this value
fmt.Printf("s3=%v",s3[4])
Copy the code
If s3 has exceeded len length, an error will be reported as follows
panic: runtime error: index out of range [4] with length 2
goroutine 1 [running]:
main.main()
E:/project/godemo/part6-slice.go:14 +0x49f
Copy the code
From the above examples, we can draw the following conclusions.
- Slice can be extended backwards, but not forwards.
- S [I] cannot go beyond len(s), and backward expansion cannot go beyond cap(s)
The extension to Slice is a bit of a pain in the neck, but it’s not a bad idea to really understand the algorithm. I hope you can understand the above explanation as well. The old cat is doing its best, but if there is any confusion, you are welcome to talk about the old cat.
Operation of slice
Add elements to Slice. How? Take a look at the old cat code, as follows:
func main(a) {
arr :=[...]int{0.1.2.3.4.5.6.7}
s1 :=arr[2:6]
s2 :=s1[3:5]
s3 := append(s2,10) / /,6,10 [5]
s4 := append(s3,11) / /,6,10,11 [5]
s5 := append(s4,12)
fmt.Printf("arr=%v\n",arr)
fmt.Printf("s2=%v,len(s2)=%d,cap(s2)=%d\n",s2,len(s2),cap(s2))
fmt.Printf("s2=%v\n",s2)
fmt.Printf("s3=%v\n",s3)
fmt.Printf("s4=%v\n",s4)
fmt.Printf("s5=%v\n",s5)
}
Copy the code
As shown above, we use the append function to add operations to slices, so you can calculate the final output without looking at the actual results below. Combine the slicing operation described earlier by the old cat. The results are as follows:
arr=[0 1 2 3 4 5 6 10]
s2=[5 6].len(s2)=2.cap(s2)=3
s2=[5 6]
s3=[5 6 10]
s4=[5 6 10 11]
s5=[5 6 10 11 12]
Copy the code
Above we will see that the append operation has this conclusion
- If you add more elements than cap, the system reallocates the larger underlying array
- Because of value passing, the return value from Append must be received
Slice creation and copy
In the past, the old cat shared a slice that looked like it was arR-based, but the underlying slice is arR-based, so should we create a new array every time we create a slice? There are several ways to create a slice. Let’s take a look at the following
func main(a) {
var s []int // Void slice is an array of Nil values
for i := 0; i<100; i++ { s =append(s,2*i+1)
}
fmt.Println(s)
s1 :=[]int {2.4.5.6} // create a slice with initialization worth
s2 :=make([]int ,16) // create a slice of length 16 using the make built-in function
s3 :=make([]int.10.32) // create a slice of length 10 with cap 32
// Slice is also fairly simple to copy using built-in functions, as shown below
copy(s2,s1) // copy s1 to s2. // copy s1 to s2
}
Copy the code
Delete the Slice element
The main reason to share the delete operation separately is that all of the above operations have convenient built-in functions to use, but the delete operation does not. We can only evaluate it by the properties of slices. The following example
func main(a) {
s1 :=[] int{2.3.4.5.6}
s2 :=append(s1[:2],s1[3:]...). fmt.Println(s2) }Copy the code
There is a slice from 2 to 6 above, if we want to remove 4 elements, then we need to remove the elements in this slice combination, I believe you can understand, as for “S1 [3:]…” And this form, which is actually a way of writing go, is to take all of the remaining elements from position three.
So what we end up with is what we get
[2 3 5 6]
Copy the code
Slice is an important part of the language.
Write in the last
To review the GO container above, I would like to share with you the definitions, operations and underlying principles of slice. It’s easier to get started if you figure it out. Of course, go language containers can be more than these, due to the limitation of space, the old cat will not share other containers, I believe in writing down will not have the patience to see. The following containers will mainly share maps and character and string handling. Welcome to pay attention to the public number “programmer old cat”