本文 AI 產出,尚未審核

Golang - 陣列、切片與映射的迭代(for‑range)

簡介

在 Go 語言中,陣列(array)切片(slice)映射(map) 是最常使用的集合型別。無論是處理資料清單、緩衝區,或是鍵值對的查詢,都離不開對這些容器的遍歷(iteration)。
Go 為了讓遍歷更簡潔、可讀,提供了 for‑range 這個語法結構。它能同時取得 索引(或鍵),且自動處理底層的指標與長度檢查,讓開發者不必手動寫 for i := 0; i < len(x); i++ 那樣的樣板程式碼。

本篇文章將從 概念實作範例常見陷阱最佳實踐 以及 實務應用 四個層面,完整說明 for‑range 在陣列、切片與映射上的使用方式,幫助初學者快速上手,也讓已有基礎的開發者能夠寫出更安全、效能更佳的程式。


核心概念

1. for‑range 的基本語法

for key, value := range collection {
    // 在此使用 key 與 value
}
  • collection 可以是陣列、切片、映射、字串或通道(channel)。
  • key:對於陣列與切片是索引(int),對於映射是鍵的類型(如 stringint)。
  • value:對應的元素值。若只需要其中之一,可使用 _ 佔位符忽略。

小技巧:若只需要值而不在意索引,寫成 for _, v := range xs { … };若只需要索引,寫成 for i := range xs { … }


2. 迭代陣列

陣列的長度在編譯期就已確定,for‑range 會依序遍歷每個元素,不會改變原始陣列

package main

import "fmt"

func main() {
    // 宣告一個長度為 5 的整數陣列
    var nums = [5]int{10, 20, 30, 40, 50}

    // 只取得值
    for _, v := range nums {
        fmt.Printf("值: %d\n", v)
    }

    // 同時取得索引與值
    for i, v := range nums {
        fmt.Printf("索引 %d => 值 %d\n", i, v)
    }
}

注意:陣列是值類型(value type),若在迭代過程中把 v 改成 v = 0不會影響 nums 本身。


3. 迭代切片

切片是基於陣列的抽象,長度與容量在執行時可變。for‑range 仍會返回 索引(相對於切片的起始位置)與

package main

import "fmt"

func main() {
    // 建立一個切片
    fruits := []string{"蘋果", "香蕉", "櫻桃"}

    // 只要值
    for _, f := range fruits {
        fmt.Println("水果:", f)
    }

    // 同時取得索引
    for i, f := range fruits {
        fmt.Printf("第 %d 個水果是 %s\n", i, f)
    }

    // 修改切片內的元素(直接改變底層陣列)
    for i := range fruits {
        fruits[i] = fruits[i] + " (好吃)"
    }
    fmt.Println("更新後:", fruits)
}

關鍵for i := range fruits 只取得索引,不會產生值的副本,因此在迴圈內直接寫 fruits[i] = … 可以改變切片內容。


4. 迭代映射(map)

映射的遍歷順序是隨機的(不保證插入順序),每次執行程式可能得到不同的順序。for‑range 會同時返回

package main

import "fmt"

func main() {
    // 建立一個 map
    scores := map[string]int{
        "Alice": 85,
        "Bob":   92,
        "Carol": 78,
    }

    // 同時取得鍵和值
    for name, score := range scores {
        fmt.Printf("%s 的分數是 %d\n", name, score)
    }

    // 只需要鍵
    for name := range scores {
        fmt.Println("玩家:", name)
    }

    // 只需要值(使用 _ 忽略鍵)
    for _, score := range scores {
        fmt.Println("分數:", score)
    }
}

提醒:因為遍歷順序不固定,若程式邏輯依賴順序(例如要產生報表),必須先把鍵抽出排序,再逐一取值。


5. for‑range 與指標、切片的結合

在某些情況下,我們想要 直接取得元素的指標,以免每次都產生值的副本。可以在迭代切片時使用指標:

package main

import "fmt"

func main() {
    nums := []int{1, 2, 3, 4, 5}
    // 取得每個元素的指標
    for i := range nums {
        p := &nums[i]      // p 為 *int
        *p = *p * 10       // 直接修改底層陣列
    }
    fmt.Println(nums) // [10 20 30 40 50]
}

最佳實踐:若僅需要讀取值,仍建議使用 value(非指標)以保持程式的可讀性與安全性;只有在大量寫入需要共享同一個變數時,才考慮使用指標。


常見陷阱與最佳實踐

陷阱 說明 建議的寫法
迭代時誤改變迭代變數 for _, v := range slice 取得的是 值的副本,若在迴圈內改 v,不會影響原 slice。 若要改變元素,使用索引:for i := range slice { slice[i] = … }
閉包捕獲迭代變數 for i, v := range xs { go func(){ fmt.Println(i, v) }() } 中,iv 會被所有 goroutine 共享,導致結果錯亂。 在迴圈內建立新變數:i, v := i, v; go func(){ … }()
map 的遍歷順序不固定 直接依序輸出 map 可能得到不同結果,影響測試或報表。 把鍵抽出切片,使用 sort.Strings(keys)(或相應類型)排序後再取值。
切片的容量擴增導致指標失效 在迭代過程中若對切片執行 append,可能觸發底層陣列重新分配,導致先前取得的指標失效。 避免在遍歷時同時 append 同一切片,或先把要加入的元素收集到另一個切片,迭代結束後一次 append
忘記使用 _ 忽略不需要的返回值 只想要索引卻寫成 for i, _ := range xs,會產生不必要的變數。 正確寫法:for i := range xsfor _, v := range xs

最佳實踐小結

  1. 盡量使用 for‑range 取代傳統 for i := 0; i < len(x); i++,提升可讀性。
  2. 只取需要的返回值,用 _ 省略其餘,減少記憶體開銷。
  3. 若要修改容器內容,使用索引或指標,避免直接改變 value 副本。
  4. 在多執行緒(goroutine)環境,務必在迴圈內建立局部變數,以防止閉包捕獲同一個迭代變數。
  5. 對 map 需要有序輸出時,先排序鍵再遍歷。

實際應用場景

場景 為何使用 for‑range 範例簡述
資料庫查詢結果轉換 查詢回傳的 []Row 需要逐筆處理並寫入另一個結構。 for _, r := range rows { dst = append(dst, convert(r)) }
統計字串出現次數 使用 map[string]int 來計數,最後遍歷產生報表。 for word, cnt := range counter { fmt.Printf("%s: %d\n", word, cnt) }
批次處理檔案 讀取目錄下的檔名切片,逐一開啟、處理。 for _, name := range files { process(filepath.Join(dir, name)) }
併發工作分配 把切片的工作項目分配給多個 goroutine,使用閉包捕獲索引時需特別處理。 for i, job := range jobs { i, job := i, job; go worker(i, job) }
JSON 序列化/反序列化 把結構切片轉成 JSON 陣列,或把 JSON 陣列解析回切片。 for _, item := range data { json.Marshal(item) }

總結

  • for‑range 是 Go 中遍歷 陣列、切片、映射 的首選語法,提供 索引/鍵 + 值 兩個返回值,讓程式碼更簡潔、易讀。
  • 迭代時要注意 值的副本 vs. 原始資料閉包捕獲變數、以及 map 的隨機順序,這些都是常見的陷阱。
  • 透過 索引指標局部變數 的正確使用,可安全地在迴圈內修改資料或啟動 goroutine。
  • 在實務開發中,for‑range 幾乎無所不在:從資料清理、統計分析、批次處理到併發工作分配,都能發揮其簡化迭代的威力。

掌握了 for‑range 的正確用法與最佳實踐,你就能在 Go 程式開發中寫出更清晰安全、且效能良好的程式碼。祝你在 Golang 的學習旅程中,迭代出更多精彩的作品!