iT邦幫忙

2025 iThome 鐵人賽

DAY 2
0
Modern Web

後端攻略筆記系列 第 2

Day 2 : Go語言 slice 與 map 進階理解

  • 分享至 

  • xImage
  •  

⚙ 篇二

一、slice 的底層原理

  • slice 本質是一個結構:
    type slice struct {
        ptr *array  // 指到底層陣列的指標
        len int     // 長度
        cap int     // 容量
    }
    
  • append 過程:
    • 若 cap 夠,用原陣列
    • 若 cap 不夠 → 配置新陣列(通常 2 倍擴容),並複製資料

🔎 陷阱:

  • 擷取子 slice 共用底層陣列,修改會互相影響。
  • append 超過 cap 時,會產生新的陣列,舊的 slice 參照不會自動更新。

二、map 的底層原理

  • Go map 用 hash table,採用開放定址 + 設計好的 bucket(桶)。
  • 取值流程:
    • 用 key hash 計算 bucket
    • bucket 里儲存了一組 key-value slot
  • 平均 O(1),最壞情況 O(n)(hash collision 全撞在同一桶)。

🔎 陷阱:

  • map 無序,甚至 Go 1.12 後官方刻意「打亂順序」避免誤用。
  • 不能直接比較 map(只能跟 nil 比較),不能複製比較相等。
  • 並發寫入 map 沒加鎖會 panic → 需要 sync.Mutex 或 sync.Map。

三、value, ok := m[key] 的內建語義

  • value:若存在,取出實際值;不存在,回「零值」
  • ok:bool,true/false
  • 雙重用途:
    • 區分「key 不存在」和「值剛好等於零值」
    • 避免誤判 0 與不存在混淆

四、slice vs map:性能與特性對比

特性 slice map
存取時間 O(1) by index O(1) 平均 by key
查找是否存在 O(n) 線性 O(1) 平均,需 ok 檢查
有序性 ✅ 有序 ❌ 無序
記憶體利用 緊湊,高快取命中率 需 hash 結構,記憶體成本較大
適合場景 排序處理、順序遍歷、大量掃描 去重、分組、快查、快取

五、誤解澄清

  • ❌ slice 像 MySQL?
    MySQL 使用 B+Tree、頁管理、索引 → 更像「磁碟資料結構 + 索引系統」,不是 slice。
  • ❌ map 就是 Redis?
    Redis 提供多種結構(string/hash/set/zset…),其中 hash 與 set 類似 Go map,但 Redis 同時有持久化、通訊協議、分散式功能。

六、常見應用模式

  • slice + sort:排好序之後用二分查找(O(log n))
  • map 做快取:如 map[string]*User 快速 ID 查詢
  • slice + map 混用:slice 保順序,map 索引定位
    type User struct {
        ID   int
        Name string
    }
    
    var users []User
    var index = map[int]*User // 快速透過 ID 查
    

七、小結

  • slice 適合「有順序的集合處理」
  • map 適合「快速查找」
  • 底層不同:slice 連續記憶體,map 雜湊存取
  • 在工程選擇上:性能和資料特性決定用誰,而不是誰比較「高級」


八、補充

Slice 跟 Map 的層次理解:
作者在了解 slice 跟 map 時 和 MySQL/Redis
有點搞混,以下寫給初學者,方便釐清不從名詞意義

層次 0:比喻先行(幫助快速直覺)

  • 記憶體 = 白板:寫了就看得到,但電源一關就沒了。
  • 磁碟/SSD = 筆記本:關機也還在,但翻找較慢。
  • slice/map = 白板上的「表格/字典」畫法,是你「在記憶體中」安排資料的方式。
  • MySQL/Redis = 圖書館系統:幫你把資料「規則化保存、建立索引、提供查詢語言、控管權限與並發」,資料會寫進「筆記本」(磁碟或持久化格式)。

層次 1:程式語言層(In-memory data structures)

  • 這一層是 Go/Java/Python 的「語言內建資料結構」:
    • slice、array、map、struct、list、set、heap…
  • 特性
    • 存在 RAM(記憶體)裡,速度快但易失(關機就沒了)。
    • 服務於「當前程式執行」的計算:排序、查找、聚合、去重、分組、暫存。
  • 關鍵句
    • slice/map 只是「記憶體資料結構」,不是檔案格式、不是資料庫。
    • 若要保存到 SSD/HDD,需要「序列化」或「交給儲存系統」。

層次 2:序列化與存取(Serialization/IO)

  • 這一層關心「如何把記憶體物件變成可落地/傳輸的位元組」:
    • JSON、CSV、Protobuf、Avro、MsgPack、Gob(Go語言)、二進位自定義格式。
  • 使用情境
    • 將 slice/map 轉成 JSON 字串寫檔、發 API、丟訊息佇列。
    • 從檔案/網路讀回來,再反序列化回程式內的 slice/map。
  • 關鍵句
    • 這是「語言資料結構 ↔ 可持久/可傳輸格式」之間的橋樑。

層次 3:儲存與索引引擎(Storage & Indexing Engines)

  • 這一層就是 MySQL、Postgres、SQLite、Redis、Elasticsearch 等系統:
    • 提供「持久化、索引、查詢語言、交易(ACID)、並發控制、授權、備份」等。
  • 內部機制
    • MySQL(InnoDB):B+Tree 索引、頁面、日誌、MVCC…
    • Redis:主要在記憶體,結構包含 hash、list、set、zset、bitmap、hyperloglog;持久化有 RDB/AOF。
    • Elasticsearch:倒排索引、段文件、合併、向量索引等。
  • 關鍵句
    • 這些系統不是「一個大的 map/slice」,而是「管理資料與查詢的完整軟體」。

層次 4:應用系統與架構(Application & Architecture)

  • 這一層在談「怎麼用」:
    • 程式在記憶體用 slice/map 做計算與暫存。
    • 計算完重要資料:序列化,或用 ORM/驅動寫入 MySQL/Redis 等。
    • 查詢時:先問資料庫;讀回結果後,再用 slice/map 做進一步處理與渲染。
  • 常見模式
    • 讀資料:DB → 反序列化 → 放入 slice/map → 業務邏輯。
    • 寫資料:slice/map → 序列化或 ORM → DB/檔案。

為什麼不能直接拿 slice/map 跟 MySQL/Redis 比?

  • 層次不同,角色不同:
    • slice/map:是「語言內部、暫時、易失」的資料容器。
    • MySQL/Redis:是「獨立服務、持久、可併發訪問、有安全與一致性機制」的資料系統。
  • 類比:不能拿「白板上的表格」去跟「圖書館管理系統」比較;前者是你怎麼寫、後者是整個保存與檢索的機制。

如何在腦中建立層次,避免搞混?

  • 問自己三個問題:
    1. 現在資料「在哪裡」?RAM 還是 SSD/HDD/遠端服務?
    2. 現在用的是「語言內建結構」還是「外部儲存系統」?
    3. 我是在做「計算/暫存」還是「持久化/多人共享/可檢索」?
  • 只要答案是「在記憶體、用語言內建、做計算」→ 想 slice/map。
  • 只要答案是「要落地、要查詢、要共享、要安全」→ 想資料庫/搜尋引擎/檔案系統。

簡短對照表

  • slice/map
    • 所在:記憶體
    • 生命:程式關掉就沒了
    • 功能:計算、暫存、快查(map)、排序(slice)
    • 存取:程式語言直接操作
  • MySQL/Redis
    • 所在:獨立服務(磁碟/記憶體持久化策略)
    • 生命:可跨重啟、可備份
    • 功能:持久化、索引、併發、交易、權限
    • 存取:SQL/協議/API

小結:

  • slice/map 是「在 RAM 裡運算的工具」;MySQL/Redis 是「管理資料、讓它可持久可查詢的系統」。兩者是上下游關係,不是同層級的替代關係。

小作業:可以自行嘗試:把一個 Go struct 切片序列化為 JSON 寫檔,再讀回來放到 slice/map,讓「層次 1→2→1」的過程變得可見。


上一篇
Day 1 : Go 語言入門:用串珠和電話簿搞懂 slice 與 map
下一篇
Day 3 : Go語言 slice 與 map 應用練習
系列文
後端攻略筆記13
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言