Maps
การสร้าง map
หัวข้อที่มีชื่อว่า “การสร้าง map”Map คือ hash table ที่ map key ชนิด K ไปยัง value ชนิด V สร้างด้วย make หรือ composite literal:
// make — เหมาะเมื่อเพิ่ม entry แบบทีละตัวscores := make(map[string]int)
// composite literal — เหมาะเมื่อรู้ entry ล่วงหน้าscores := map[string]int{ "Alice": 95, "Bob": 87,}Key type ต้องเป็น comparable (รองรับ ==) type ที่ใช้เป็น key ได้คือ string, int, ตัวเลขทุกชนิด, boolean, pointer, และ struct ที่ field ทุกตัวเป็น comparable แต่ slice, map, และ function ไม่สามารถใช้เป็น key ได้
Set, get, delete
หัวข้อที่มีชื่อว่า “Set, get, delete”scores["Charlie"] = 72 // setfmt.Println(scores["Alice"]) // get → 95delete(scores, "Bob") // delete (ไม่มีผลถ้า key ไม่มีอยู่)Zero value สำหรับ key ที่ไม่มีอยู่
หัวข้อที่มีชื่อว่า “Zero value สำหรับ key ที่ไม่มีอยู่”การอ่าน key ที่ไม่มีอยู่ ไม่ panic เลย — จะคืน zero value ของ value type:
fmt.Println(scores["Dave"]) // 0 — ไม่ใช่ panicแม้จะสะดวก แต่ก็ซ่อนบั๊กได้: คุณไม่รู้ว่า key map ไปที่ 0 หรือไม่เคยถูก set ใช้ comma-ok idiom เพื่อแยกแยะ
Comma-ok idiom
หัวข้อที่มีชื่อว่า “Comma-ok idiom”การ lookup map แบบสอง result คืนค่าและ boolean ที่บอกว่า key มีอยู่หรือไม่:
v, ok := scores["Dave"]if !ok { fmt.Println("Dave not found")}Map เป็น reference type
หัวข้อที่มีชื่อว่า “Map เป็น reference type”การ assign map จะ copy แค่ header ไม่ใช่ข้อมูล ตัวแปรทั้งสองชี้ไปยัง hash table เดียวกัน:
a := map[string]int{"x": 1}b := ab["x"] = 99fmt.Println(a["x"]) // 99 — a และ b แชร์ map เดียวกันลำดับ iteration แบบสุ่ม
หัวข้อที่มีชื่อว่า “ลำดับ iteration แบบสุ่ม”Go จงใจสุ่มลำดับ map iteration ทุกครั้งที่รันโปรแกรม เพื่อป้องกันโค้ดที่พึ่งพาลำดับที่ไม่ได้นิยาม หากต้องการ print entry ของ map แบบ deterministic ให้ sort key ก่อน:
import "sort"
keys := make([]string, 0, len(scores))for k := range scores { keys = append(keys, k)}sort.Strings(keys)for _, k := range keys { fmt.Printf("%s: %d\n", k, scores[k])}package main
import ( "fmt" "sort")
func main() { // make a map and add entries scores := make(map[string]int) scores["Alice"] = 95 scores["Bob"] = 87 scores["Charlie"] = 72
// Comma-ok idiom v, ok := scores["Bob"] fmt.Println(v, ok) // 87 true
v2, ok2 := scores["Dave"] fmt.Println(v2, ok2) // 0 false
// Delete an entry delete(scores, "Charlie")
// Sort keys for deterministic output keys := make([]string, 0, len(scores)) for k := range scores { keys = append(keys, k) } sort.Strings(keys) for _, k := range keys { fmt.Printf("%s: %d\n", k, scores[k]) }}Loading Go runtime (first run only, ~8 MB)…
ข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ข้อแลกเปลี่ยน”| สิ่งที่ได้ | ประโยชน์ | ต้นทุน |
|---|---|---|
| built-in hash map | O(1) average lookup, ใช้งานง่าย | ไม่ thread-safe — ต้องใช้ mutex หรือ sync.Map |
| zero value for missing key | ไม่ panic เมื่ออ่าน key ที่ไม่มี | ซ่อน bug ถ้าลืมใช้ comma-ok idiom |
| dynamic sizing | map ขยายอัตโนมัติ | map ไม่คืน memory หลัง delete — ต้องสร้างใหม่ถ้าต้องการ shrink |
| randomized iteration | ป้องกัน code ที่พึ่งพา order โดยไม่ตั้งใจ | ต้อง sort key ถ้าต้องการ deterministic output |
ความเข้าใจผิดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ความเข้าใจผิดที่พบบ่อย”- map safe สำหรับ concurrent access — map ไม่ใช่ — goroutine หลายตัวอ่าน/เขียนพร้อมกันทำให้เกิด race condition และ crash
- iteration order ของ map เป็น insertion order — Go map iterate แบบ random order โดยเจตนา ทุก run อาจต่างกัน
delete()ลด memory ทันที — Go map ไม่คืน memory ที่ใช้กับ bucket หลัง delete — สร้าง map ใหม่ถ้าต้องการ shrinklen(m)slow เหมือน iterate —len(map)เป็น O(1) Go track count ไว้ใน map header
💡 ตัวอย่างจากของจริง
Kubernetes ใช้
map[string]stringสำหรับ pod label, annotation, และ config — label selector ก็เป็น map เช่นกันHTTP servers ใช้
map[string][]stringสำหรับ HTTP headers (http.Header) — protect ด้วย mutex ใน concurrent handler