金色的鱼钩 發表於 2025-12-23 10:33:02

Go语言中范围range的使用小结

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li><a href="#_label0">一、什么是range</a></li><li><a href="#_label1">二、遍历数组 / 切片(最常用)</a></li><li><a href="#_label2">三、遍历字符串</a></li><li><a href="#_label3">四、遍历 map</a></li><li><a href="#_label4">五、遍历 channel</a></li><li><a href="#_label5">六、range 的&ldquo;值拷贝陷阱&rdquo;</a></li><li><a href="#_label6">七、什么时候不用 range?</a></li><li><a href="#_label7">八、总结</a></li></ul></div><p class="maodian"><a name="_label0"></a></p><h2>一、什么是range</h2>
<p><code>range</code> 是 Go 里 <strong>用于遍历数据结构的语法糖</strong>,常见用法:</p>
<div class="jb51code"><pre class="brush:go;">for k, v := range collection {
    ...
}
</pre></div>
<p>它能遍历的对象包括:</p>
<ul><li>数组 / 切片</li><li>字符串</li><li>map</li><li>channel</li></ul>
<p>本质上:<strong>每次循环,range 会返回&ldquo;索引/键 + 值&rdquo;</strong></p>
<p class="maodian"><a name="_label1"></a></p><h2>二、遍历数组 / 切片(最常用)</h2>
<div class="jb51code"><pre class="brush:go;">arr := []int{10, 20, 30}

for i, v := range arr {
    fmt.Println(i, v)
}
</pre></div>
<p>输出:</p>
<blockquote><p>0 10<br />1 20<br />2 30</p></blockquote>
<p>只要值,不要索引</p>
<div class="jb51code"><pre class="brush:go;">for _, v := range arr {
    fmt.Println(v)
}
</pre></div>
<p>只要索引</p>
<div class="jb51code"><pre class="brush:go;">for i := range arr {
    fmt.Println(i)
}
</pre></div>
<p class="maodian"><a name="_label2"></a></p><h2>三、遍历字符串</h2>
<div class="jb51code"><pre class="brush:go;">s := "你好,go!"
for i, c := range s {
        fmt.Printf("index=%d,char=%c\n", i, c)
}</pre></div>
<p>输出:</p>
<blockquote><p>index=0,char=你<br />index=3,char=好<br />index=6,char=,<br />index=9,char=g<br />index=10,char=o<br />index=11,char=!</p></blockquote>
<p>注意:</p>
<ul><li><code>i</code>:<strong>字节索引(不是字符索引)</strong></li><li><code>c</code>:<code>rune</code>(Unicode 字符)</li></ul>
<p>对比 Java</p>
<table><thead><tr><th>Java</th><th>Go</th></tr></thead><tbody><tr><td>char 是 UTF-16</td><td>rune 是 UTF-32</td></tr><tr><td>String.length() 不等于字符数</td><td>len(s) 是字节数</td></tr><tr><td>遍历字符较绕</td><td>range 天然支持 Unicode</td></tr></tbody></table>
<p><strong>遍历中文,Go 用 range 是&ldquo;正解&rdquo;</strong></p>
<p class="maodian"><a name="_label3"></a></p><h2>四、遍历 map</h2>
<div class="jb51code"><pre class="brush:go;">m := mapint{
    "a": 1,
    "b": 2,
}

for k, v := range m {
    fmt.Println(k, v)
}
</pre></div>
<p><strong>重要特性</strong></p>
<ul><li>map 遍历 <strong>无序</strong></li><li>每次运行顺序可能不一样</li><li>Go 刻意这么设计(防止依赖顺序的 bug)</li></ul>
<p>如果需要有序,需这样处理:</p>
<div class="jb51code"><pre class="brush:go;">keys := make([]string, 0, len(m))
for k := range m {
    keys = append(keys, k)
}
sort.Strings(keys)

for _, k := range keys {
    fmt.Println(k, m)
}
</pre></div>
<p class="maodian"><a name="_label4"></a></p><h2>五、遍历 channel</h2>
<p>在并发中很有用</p>
<div class="jb51code"><pre class="brush:go;">ch := make(chan int)

go func() {
    ch &lt;- 1
    ch &lt;- 2
    close(ch)
}()

for v := range ch {
    fmt.Println(v)
}
</pre></div>
<p>特点:</p>
<ul><li>会 <strong>一直阻塞</strong></li><li>直到 <code>channel</code> 被 <code>close</code></li><li>非常适合消费协程产生的数据</li></ul>
<p class="maodian"><a name="_label5"></a></p><h2>六、range 的&ldquo;值拷贝陷阱&rdquo;</h2>
<p>问题代码</p>
<div class="jb51code"><pre class="brush:go;">arr := []int{1, 2, 3}

for _, v := range arr {
    v = v * 10
}

fmt.Println(arr) //
</pre></div>
<p><strong>为什么没改?</strong></p>
<ul><li><code>v</code> 是 <strong>元素的拷贝</strong></li><li>修改 <code>v</code> 不影响原数组</li></ul>
<p>正确写法</p>
<div class="jb51code"><pre class="brush:go;">for i := range arr {
    arr *= 10
}
</pre></div>
<p class="maodian"><a name="_label6"></a></p><h2>七、什么时候不用 range?</h2>
<table><thead><tr><th>场景</th><th>建议</th></tr></thead><tbody><tr><td>需要修改原数组</td><td>用索引 for</td></tr><tr><td>需要精确控制步长</td><td>用经典 for</td></tr><tr><td>性能极限场景</td><td>手写 for 更可控</td></tr><tr><td>遍历 map 顺序敏感</td><td>不适合</td></tr></tbody></table>
<p class="maodian"><a name="_label7"></a></p><h2>八、总结</h2>
<blockquote><p>Go 的 range = 更安全、更简洁的 for-each,但要记住:</p>
<p>1. 值是拷贝;</p>
<p>2. map 无序;</p>
<p>3. 字符串按 rune。</p></blockquote>
頁: [1]
查看完整版本: Go语言中范围range的使用小结