/* 04-range.go - Go 语言 range 关键字详解 学习目标: 1. 掌握 range 的基本用法 2. 理解 range 在不同数据类型上的应用 3. 学会处理 range 返回的索引和值 4. 了解 range 的性能特点 5. 掌握 range 的实际应用场景 知识点: - range 遍历数组和切片 - range 遍历字符串 - range 遍历映射 (map) - range 遍历通道 (channel) - range 的值拷贝特性 - 忽略索引或值的技巧 - range 的性能考虑 */ package main import ( "fmt" "unicode/utf8" ) func main() { fmt.Println("=== Go 语言 range 关键字详解 ===\n") // 演示 range 遍历数组和切片 demonstrateRangeArraySlice() // 演示 range 遍历字符串 demonstrateRangeString() // 演示 range 遍历映射 demonstrateRangeMap() // 演示 range 遍历通道 demonstrateRangeChannel() // 演示 range 的值拷贝特性 demonstrateRangeValueCopy() // 演示忽略索引或值 demonstrateIgnoreIndexValue() // 演示 range 的实际应用 demonstratePracticalExamples() // 演示 range 的性能考虑 demonstratePerformanceConsiderations() } // demonstrateRangeArraySlice 演示 range 遍历数组和切片 func demonstrateRangeArraySlice() { fmt.Println("1. range 遍历数组和切片:") // 遍历数组 fmt.Printf(" 遍历数组:\n") arr := [5]int{10, 20, 30, 40, 50} for index, value := range arr { fmt.Printf(" arr[%d] = %d\n", index, value) } // 遍历切片 fmt.Printf(" 遍历切片:\n") slice := []string{"Go", "Python", "Java", "JavaScript"} for i, lang := range slice { fmt.Printf(" 语言 %d: %s\n", i+1, lang) } // 遍历空切片 fmt.Printf(" 遍历空切片:\n") var emptySlice []int count := 0 for range emptySlice { count++ } fmt.Printf(" 空切片遍历次数: %d\n", count) // 遍历 nil 切片 fmt.Printf(" 遍历 nil 切片:\n") var nilSlice []int = nil count = 0 for range nilSlice { count++ } fmt.Printf(" nil 切片遍历次数: %d\n", count) // 多维切片遍历 fmt.Printf(" 遍历二维切片:\n") matrix := [][]int{ {1, 2, 3}, {4, 5, 6}, {7, 8, 9}, } for i, row := range matrix { for j, value := range row { fmt.Printf(" matrix[%d][%d] = %d\n", i, j, value) } } fmt.Println() } // demonstrateRangeString 演示 range 遍历字符串 func demonstrateRangeString() { fmt.Println("2. range 遍历字符串:") // 基本字符串遍历 fmt.Printf(" 遍历 ASCII 字符串:\n") str1 := "Hello" for index, char := range str1 { fmt.Printf(" str1[%d] = '%c' (Unicode: %d)\n", index, char, char) } // 遍历包含中文的字符串 fmt.Printf(" 遍历包含中文的字符串:\n") str2 := "Hello世界" for index, char := range str2 { fmt.Printf(" 位置 %d: '%c' (Unicode: %d)\n", index, char, char) } // 注意:index 是字节位置,不是字符位置 fmt.Printf(" 注意: range 返回的索引是字节位置,不是字符位置\n") // 字符串长度对比 fmt.Printf(" 字符串长度对比:\n") fmt.Printf(" 字节长度 len(\"%s\"): %d\n", str2, len(str2)) fmt.Printf(" 字符长度 utf8.RuneCountInString(\"%s\"): %d\n", str2, utf8.RuneCountInString(str2)) // 遍历字符串的字节 fmt.Printf(" 按字节遍历字符串:\n") for i := 0; i < len(str2); i++ { fmt.Printf(" 字节 %d: %d ('%c')\n", i, str2[i], str2[i]) if i >= 7 { // 限制输出 fmt.Printf(" ...\n") break } } // 统计字符类型 fmt.Printf(" 统计字符类型:\n") text := "Hello, 世界! 123" ascii, unicode, digit, other := 0, 0, 0, 0 for _, char := range text { switch { case char >= 'A' && char <= 'Z' || char >= 'a' && char <= 'z': ascii++ case char >= '0' && char <= '9': digit++ case char > 127: unicode++ default: other++ } } fmt.Printf(" 文本: \"%s\"\n", text) fmt.Printf(" ASCII字母: %d, Unicode字符: %d, 数字: %d, 其他: %d\n", ascii, unicode, digit, other) fmt.Println() } // demonstrateRangeMap 演示 range 遍历映射 func demonstrateRangeMap() { fmt.Println("3. range 遍历映射 (map):") // 基本映射遍历 fmt.Printf(" 遍历基本映射:\n") scores := map[string]int{ "Alice": 95, "Bob": 87, "Charlie": 92, "David": 78, } for name, score := range scores { fmt.Printf(" %s: %d分\n", name, score) } // 注意:映射遍历顺序是随机的 fmt.Printf(" 注意: 映射的遍历顺序是随机的\n") // 遍历复杂映射 fmt.Printf(" 遍历复杂映射:\n") students := map[string]map[string]interface{}{ "student1": { "name": "张三", "age": 20, "grade": "A", }, "student2": { "name": "李四", "age": 21, "grade": "B", }, } for id, info := range students { fmt.Printf(" 学生ID: %s\n", id) for key, value := range info { fmt.Printf(" %s: %v\n", key, value) } } // 统计映射信息 fmt.Printf(" 统计映射信息:\n") wordCount := map[string]int{ "go": 10, "python": 8, "java": 15, "rust": 5, } totalWords := 0 maxCount := 0 mostPopular := "" for word, count := range wordCount { totalWords += count if count > maxCount { maxCount = count mostPopular = word } } fmt.Printf(" 词汇统计: %v\n", wordCount) fmt.Printf(" 总词数: %d\n", totalWords) fmt.Printf(" 最受欢迎的词: %s (%d次)\n", mostPopular, maxCount) // 遍历空映射 fmt.Printf(" 遍历空映射:\n") emptyMap := make(map[string]int) count := 0 for range emptyMap { count++ } fmt.Printf(" 空映射遍历次数: %d\n", count) fmt.Println() } // demonstrateRangeChannel 演示 range 遍历通道 func demonstrateRangeChannel() { fmt.Println("4. range 遍历通道 (channel):") fmt.Printf(" 基本通道遍历:\n") // 创建一个通道并发送数据 ch := make(chan int, 5) // 发送数据到通道 for i := 1; i <= 5; i++ { ch <- i * 10 } close(ch) // 关闭通道,range 才能正常结束 // 使用 range 遍历通道 for value := range ch { fmt.Printf(" 从通道接收: %d\n", value) } // 字符串通道示例 fmt.Printf(" 字符串通道遍历:\n") strCh := make(chan string, 3) messages := []string{"Hello", "World", "Go"} for _, msg := range messages { strCh <- msg } close(strCh) for message := range strCh { fmt.Printf(" 消息: %s\n", message) } // 注意:如果通道没有关闭,range 会一直等待 fmt.Printf(" 注意: 通道必须关闭,否则 range 会一直等待\n") fmt.Println() } // demonstrateRangeValueCopy 演示 range 的值拷贝特性 func demonstrateRangeValueCopy() { fmt.Println("5. range 的值拷贝特性:") fmt.Printf(" range 会拷贝值,修改拷贝不会影响原始数据:\n") // 结构体切片示例 type Person struct { Name string Age int } people := []Person{ {"Alice", 25}, {"Bob", 30}, {"Charlie", 35}, } fmt.Printf(" 原始数据:\n") for i, person := range people { fmt.Printf(" people[%d]: %+v\n", i, person) } // 尝试修改 range 返回的值(不会影响原始数据) fmt.Printf(" 尝试修改 range 返回的值:\n") for i, person := range people { person.Age += 10 // 这不会修改原始数据 fmt.Printf(" 修改后的拷贝 people[%d]: %+v\n", i, person) } fmt.Printf(" 原始数据(未改变):\n") for i, person := range people { fmt.Printf(" people[%d]: %+v\n", i, person) } // 正确的修改方式:使用索引 fmt.Printf(" 正确的修改方式(使用索引):\n") for i := range people { people[i].Age += 5 } for i, person := range people { fmt.Printf(" people[%d]: %+v\n", i, person) } // 指针切片的情况 fmt.Printf(" 指针切片的情况:\n") ptrPeople := []*Person{ {"David", 28}, {"Eve", 32}, } // 通过指针修改(会影响原始数据) for _, personPtr := range ptrPeople { personPtr.Age += 2 } for i, personPtr := range ptrPeople { fmt.Printf(" ptrPeople[%d]: %+v\n", i, *personPtr) } fmt.Println() } // demonstrateIgnoreIndexValue 演示忽略索引或值 func demonstrateIgnoreIndexValue() { fmt.Println("6. 忽略索引或值:") // 只需要值,忽略索引 fmt.Printf(" 只需要值,忽略索引(使用 _ ):\n") numbers := []int{1, 4, 9, 16, 25} sum := 0 for _, value := range numbers { sum += value } fmt.Printf(" 数组 %v 的和: %d\n", numbers, sum) // 只需要索引,忽略值 fmt.Printf(" 只需要索引,忽略值:\n") items := []string{"apple", "banana", "cherry", "date"} for index := range items { fmt.Printf(" 索引 %d\n", index) } // 既不需要索引也不需要值(只是计数) fmt.Printf(" 只计算元素个数:\n") data := []float64{1.1, 2.2, 3.3, 4.4, 5.5} count := 0 for range data { count++ } fmt.Printf(" 元素个数: %d\n", count) // 映射中忽略键或值 fmt.Printf(" 映射中忽略键或值:\n") grades := map[string]int{ "Math": 90, "English": 85, "Science": 92, } // 只要值 fmt.Printf(" 所有分数: ") for _, score := range grades { fmt.Printf("%d ", score) } fmt.Printf("\n") // 只要键 fmt.Printf(" 所有科目: ") for subject := range grades { fmt.Printf("%s ", subject) } fmt.Printf("\n") fmt.Println() } // demonstratePracticalExamples 演示 range 的实际应用 func demonstratePracticalExamples() { fmt.Println("7. range 的实际应用:") // 示例1: 数据统计 fmt.Printf(" 示例1 - 销售数据统计:\n") sales := map[string][]int{ "Q1": {100, 120, 110, 130}, "Q2": {140, 135, 150, 145}, "Q3": {160, 155, 170, 165}, "Q4": {180, 175, 190, 185}, } yearTotal := 0 for quarter, monthlySales := range sales { quarterTotal := 0 for _, monthSale := range monthlySales { quarterTotal += monthSale } yearTotal += quarterTotal fmt.Printf(" %s 季度总销售额: %d\n", quarter, quarterTotal) } fmt.Printf(" 全年总销售额: %d\n", yearTotal) // 示例2: 配置文件处理 fmt.Printf(" 示例2 - 配置文件处理:\n") config := map[string]interface{}{ "server_port": 8080, "debug_mode": true, "database_url": "localhost:5432", "max_connections": 100, "timeout": 30.5, } for key, value := range config { fmt.Printf(" 配置项 %s: ", key) switch v := value.(type) { case int: fmt.Printf("%d (整数)\n", v) case bool: fmt.Printf("%t (布尔值)\n", v) case string: fmt.Printf("\"%s\" (字符串)\n", v) case float64: fmt.Printf("%.1f (浮点数)\n", v) default: fmt.Printf("%v (未知类型)\n", v) } } // 示例3: 文本处理 fmt.Printf(" 示例3 - 单词频率统计:\n") text := "go is great go is simple go is fast" words := []string{"go", "is", "great", "go", "is", "simple", "go", "is", "fast"} wordFreq := make(map[string]int) for _, word := range words { wordFreq[word]++ } fmt.Printf(" 文本: \"%s\"\n", text) fmt.Printf(" 词频统计:\n") for word, freq := range wordFreq { fmt.Printf(" \"%s\": %d次\n", word, freq) } // 示例4: 数据验证 fmt.Printf(" 示例4 - 用户数据验证:\n") users := []map[string]string{ {"name": "Alice", "email": "alice@example.com", "age": "25"}, {"name": "", "email": "bob@test.com", "age": "30"}, {"name": "Charlie", "email": "invalid-email", "age": "abc"}, {"name": "David", "email": "david@domain.org", "age": "28"}, } validUsers := 0 for i, user := range users { fmt.Printf(" 用户 %d: ", i+1) isValid := true // 验证姓名 if user["name"] == "" { fmt.Printf("姓名为空 ") isValid = false } // 验证邮箱(简单验证) email := user["email"] if !contains(email, "@") || !contains(email, ".") { fmt.Printf("邮箱无效 ") isValid = false } // 验证年龄 age := user["age"] if !isNumeric(age) { fmt.Printf("年龄无效 ") isValid = false } if isValid { fmt.Printf("✓ 有效用户\n") validUsers++ } else { fmt.Printf("✗ 无效用户\n") } } fmt.Printf(" 有效用户数: %d/%d\n", validUsers, len(users)) // 示例5: 矩阵操作 fmt.Printf(" 示例5 - 矩阵转置:\n") matrix := [][]int{ {1, 2, 3}, {4, 5, 6}, } fmt.Printf(" 原矩阵:\n") for i, row := range matrix { fmt.Printf(" 行 %d: %v\n", i, row) } // 创建转置矩阵 if len(matrix) > 0 { transposed := make([][]int, len(matrix[0])) for i := range transposed { transposed[i] = make([]int, len(matrix)) } for i, row := range matrix { for j, value := range row { transposed[j][i] = value } } fmt.Printf(" 转置矩阵:\n") for i, row := range transposed { fmt.Printf(" 行 %d: %v\n", i, row) } } fmt.Println() } // demonstratePerformanceConsiderations 演示 range 的性能考虑 func demonstratePerformanceConsiderations() { fmt.Println("8. range 的性能考虑:") // 大切片的遍历 fmt.Printf(" 大切片的遍历性能:\n") largeSlice := make([]int, 1000) for i := range largeSlice { largeSlice[i] = i } // 使用 range(推荐) sum1 := 0 for _, value := range largeSlice { sum1 += value if sum1 > 100 { // 提前退出演示 break } } fmt.Printf(" 使用 range 遍历(推荐): 部分和 = %d\n", sum1) // 使用传统 for 循环 sum2 := 0 for i := 0; i < len(largeSlice); i++ { sum2 += largeSlice[i] if sum2 > 100 { break } } fmt.Printf(" 使用传统 for 循环: 部分和 = %d\n", sum2) // 字符串遍历的性能 fmt.Printf(" 字符串遍历的性能考虑:\n") longString := "这是一个包含中文字符的长字符串,用于演示遍历性能" // 使用 range(处理 Unicode 正确) charCount1 := 0 for range longString { charCount1++ } fmt.Printf(" 使用 range 统计字符数: %d\n", charCount1) // 使用 len(字节数,不是字符数) byteCount := len(longString) fmt.Printf(" 使用 len 统计字节数: %d\n", byteCount) // 使用 utf8.RuneCountInString(正确的字符数) charCount2 := utf8.RuneCountInString(longString) fmt.Printf(" 使用 utf8.RuneCountInString 统计字符数: %d\n", charCount2) // 映射遍历的注意事项 fmt.Printf(" 映射遍历的注意事项:\n") largeMap := make(map[int]string) for i := 0; i < 10; i++ { largeMap[i] = fmt.Sprintf("value_%d", i) } fmt.Printf(" 映射大小: %d\n", len(largeMap)) fmt.Printf(" 映射遍历顺序是随机的,每次运行可能不同\n") // 显示前几个元素 count := 0 for key, value := range largeMap { fmt.Printf(" %d: %s\n", key, value) count++ if count >= 3 { fmt.Printf(" ...\n") break } } fmt.Println() } // 辅助函数 func contains(s, substr string) bool { for i := 0; i <= len(s)-len(substr); i++ { if s[i:i+len(substr)] == substr { return true } } return false } func isNumeric(s string) bool { if len(s) == 0 { return false } for _, char := range s { if char < '0' || char > '9' { return false } } return true } /* 运行这个程序: go run 04-range.go 学习要点: 1. range 是 Go 中遍历集合类型的标准方式 2. range 可以用于数组、切片、字符串、映射和通道 3. range 返回索引/键和值,可以用 _ 忽略不需要的部分 4. range 会拷贝值,修改拷贝不会影响原始数据 5. 字符串的 range 遍历按 Unicode 字符,索引是字节位置 range 的语法: - for index, value := range collection { ... } - for index := range collection { ... } // 只要索引/键 - for _, value := range collection { ... } // 只要值 - for range collection { ... } // 只计数 不同类型的 range: 1. 数组/切片: 返回索引和值 2. 字符串: 返回字节位置和 Unicode 字符 3. 映射: 返回键和值(顺序随机) 4. 通道: 返回接收到的值(通道需要关闭) 性能考虑: 1. range 通常比传统 for 循环更高效和安全 2. 字符串的 range 正确处理 Unicode 字符 3. 大集合的遍历考虑提前退出 4. 映射遍历顺序是随机的 最佳实践: 1. 优先使用 range 遍历集合 2. 使用 _ 忽略不需要的返回值 3. 注意 range 的值拷贝特性 4. 字符串处理时考虑 Unicode 字符 5. 通道遍历前确保通道会被关闭 常见应用场景: 1. 数据处理和统计 2. 配置文件解析 3. 文本分析 4. 数据验证 5. 矩阵操作 6. 集合操作 */