1. Introduction
In Go language, we often need to traverse the data set. For arrays, it is easy to complete traversal using for statements. However, when we face other data types such as map, string, and channel, using a normal for loop cannot directly complete the traversal. In order to traverse these data types more conveniently, Go language introduces the for...range statement. This article will start with array traversal and gradually introduce the application of for...range statements in different data types.
2. Problem introduction
Suppose we have an array of integers and we want to iterate over each element in the array and process it. In this case, we can use the for statement to combine the length of the array to implement traversal, for example:
package main import "fmt" func main() { numbers := [5]int{1, 2, 3, 4, 5} for i := 0; i < len(numbers); i++ { (numbers[i]) } }
In the above code, we define an array of integersnumbers
, iterates through the array through a normal for loop and prints each element. However, when we encounter other data types,map
、string
orchannel
When, use it at this timefor
A statement will not be able to simply traverse it. So what is the way to complete it easilymap
,string
What about traversals of other types?
In fact,go
Exist in the languagefor....range
Statements can realize traversal of these types. Let's introduce them carefully below.for...range
。
3. Basic introduction
In Go,for...range
Statements provide a convenient way to traverse data structures such as arrays, slices, maps, and channels. It hides the details of the underlying index or iterator, and is an elegant and concise syntax sugar provided by Go for traversing various data structures, making traversal operations more convenient and intuitive. Below is a detailed introduction to usefor...range
Complete the traversal operation of slices, maps, and channels.
3.1 Traversing slices
When usingfor...range
When a statement traverses a slice, it will iterate over the elements in the slice one by one and assign the index and corresponding values to the specified variable. The sample code is as follows:
numbers := [5]int{1, 2, 3, 4, 5} for index, value := range numbers { // Handle index and value here}
innumbers
It is the slice we want to traverse.index
is a variable that is assigned to the index of the current element in each iteration (starting from 0).value
is a variable that is assigned to the value of the current element in each iteration.
If you only focus on the values in the slice without the need for an index, you can use an underscore_
Alternate to index variable name to ignore it:
numbers := []int{1, 2, 3, 4, 5} for _, value := range numbers { ("Value:", value) }
In this way, the loop body will only print out the values in the slice without displaying the index.
passfor...range
By traversing the slice, we can access each element in the slice concisely and intuitively without manually managing the index, making the code more concise and easy to read.
3.2 traversing map
When usingfor...range
Statement traversalmap
When it iterates over each key-value pair in the map and assigns the key and corresponding values to the specified variable. The sample code is as follows:
students := map[string]int{ "Alice": 25, "Bob": 27, "Charlie": 23, } for key, value := range students { // Handle key and value here}
herefor...range
It will traverse all key-value pairs, and we can complete the traversal operation of the map without manually processing the iterator logic.
3.3 Traversing string
When usingfor...range
When a statement traverses a string, it iterates over the characters in the string one by one, and assigns the index and value of each character to the specified variable. Here is an example code for traversing strings:
text := "Hello, the world!" for index, character := range text { ("Index: %d, Character: %c\n", index, character) }
The output result is:
Index: 0, Character: H
Index: 1, Character: e
Index: 2, Character: l
Index: 3, Character: l
Index: 4, Character: o
Index: 5, Character: ,
Index: 6, Character:
Index: 7, Character: World
Index: 10, Character:
It should be noted that strings in Go are stored in UTF-8 encoding, which is a variable-length encoding, and different Unicode characters may occupy different numbers of bytes. andindex
The value of the character indicates the byte index position of each character in the string, so the index position of the character is not necessarily continuous.
Passed herefor...range
When statements traverse strings, we can easily handle each character without manually managing index and character encoding issues, making the logic of handling strings more concise and easy to read.
3.4 Traversing the channel
When using the for...range statement traversalchannel
When it it iterates over each value in the channel until the channel is closed. Here is a sample code:
ch := make(chan int) // Example of writing data to the channelgo func() { ch <- 1 ch <- 2 ch <- 3 close(ch) }() // will output 1 2 3for value := range ch { ("Value:", value) }
In the example, we write 3 integer values to the channel. Then, usefor...range
The statement traverses the channel, obtains each value from it and processes it.
It should be noted that if no data is available in the channel,for...range
Statements block until data is available or the channel is closed. Therefore, when there is no data in the channel, it will wait for the data to arrive.
passfor...range
The statement traversal can be very convenient to continuouslychannel
and process the data.
4. Things to note
for...range
The statement can be considered asgo
A syntactic sugar of the language simplifies our traversal operations on different data structures, but usesfor...range
There are still some precautions for statements. A full understanding of these precautions can enable us to better use this feature. We will describe it below.
4.1 Iterative variables will be reused
When usingfor...range
When looping, iterative variables will be reused. This means that in each loop iteration the iteration variable will be reused instead of creating a new iteration variable in each iteration.
Here is a simple example code that demonstrates the reused iterative variables:
package main import "fmt" func main() { numbers := []int{1, 2, 3, 4, 5} for _, value := range numbers { go func() { ((value) + " ") }() } }
In the above code, we usefor...range
Loop through slicesnumbers
, and create an anonymous function in each loop iteration and start a goroutine. This anonymous function prints the current iteration ofvalue
variable. Here is a possible result:
4 5 5 5 5
The reason for this result is that because iteration variables are reused, all goroutines will share the samevalue
variable. When goroutines start executing, they may read the result of the last iteration instead of the expected iteration order. This results in the output that may be duplicate numbers or not output in the expected order.
If it is not clear about the characteristics of the iterative variables being reused, this may lead to the occurrence of unexpected results in some scenarios. Therefore, iffor...range
When there are concurrent operations, delay functions and other operations in the loop, it also depends on the value of iterated variables. At this time, it is necessary to ensure that a new copy is created in the loop iteration to avoid unexpected results.
4.2 Copy data of range expressions participating in iteration
forfor...range
Loop is to iterate using copy data of range expressions. This means that the modification of the original data during the iteration process will not affect the results of the iteration. A simple code example is as follows:
package main import "fmt" func main() { numbers := [5]int{1, 2, 3, 4, 5} for i, v := range numbers { if i == 0 { numbers[1] = 100 // Modify the value of the original data numbers[2] = 200 } ("Index:", i, "Value:", v) } }
In the above code, we usefor...range
Loop through the arraynumbers
, and then the value of the elements in the array is modified in the loop body. The traversal results are as follows:
Index: 0 Value: 1
Index: 1 Value: 2
Index: 2 Value: 3
Index: 3 Value: 4
Index: 4 Value: 5
It can be seen that although during the iteration process,numbers
It performs traversal, but does not affect the results of traversal. From this we can also prove that those involved in iteration arerange
Replica data of expression, not replica data.
If the operation in the loop needs to rely on the intermediate modified data results, it is best to divide it into two traversals, first traversal, modify the data in it, and then traversal the modified data. The above code is improved as follows:
numbers := [5]int{1, 2, 3, 4, 5} // 1. The first traversal to modify the datafor i, _ := range numbers { if i == 0 { numbers[1] = 100 // Modify the value of the original data numbers[2] = 200 } } // 2. The second traversal output datafor i, v := range numbers { ("Index:", i, "Value:", v) }
The result of this traversal is the modified data, as follows:
Index: 0 Value: 1
Index: 1 Value: 100
Index: 2 Value: 200
Index: 3 Value: 4
Index: 4 Value: 5
4.3 The map traversal order is uncertain
For map types in Go, the order of traversing their key-value pairs is uncertain. Here is an example of a simple code:
package main import "fmt" func main() { data := map[string]int{ "apple": 1, "banana": 2, "cherry": 3, } for key, value := range data { (key, value) } }
Run the above code and the result of each output may be different, that is, the order of key-value pairs is uncertain. It is possible that the result of the first run is:
banana 2
cherry 3
apple 1
Then the result of the second run is different from the result of the first run, which may be:
apple 1
banana 2
cherry 3
From this example, we can prove that the traversal order of traversal on maps is not fixed, so we need to note that we cannot rely on the traversal order of maps.
If the data in the map needs to be output in a certain order each time, you can save the keys to the slice first, sort the slices in the specified order, then iterate over the sorted slices, and use the keys in the slice to access the value in the map. At this time, the data in the map can be output in the specified order. The following is a simple code example:
package main import ( "fmt" "sort" ) func main() { data := map[string]int{ "apple": 1, "banana": 2, "cherry": 3, } // Create a slice of save key keys := make([]string, 0, len(data)) for key := range data { keys = append(keys, key) } // Sort the slices (keys) // traverse map by sorted key for _, key := range keys { value := data[key] (key, value) } }
5. Summary
This article discusses Go languagefor...range
After a basic introduction, we first start from a simple traversal problem and discover the basic onesfor
The statement seems to be impossible to implement simplystring
,map
traversal operations of other types lead tofor...range
Statement.
Then we will introduce how to use itfor...range
rightstring
,map
,channel
traversal operations of other types. Then we'll introduce the usefor...range
Three notes, such as the copy data participating in the iteration of the range expression. By understanding these precautions, we can use them betterfor...range
Statements to avoid unexpected situations.
The above is an in-depth exploration of the detailed content of for range statements in Go. For more information about Go for range, please follow my other related articles!