Arrays, Slices และ Maps ในภาษา Go

Waratep Tubboon
Stories of Sellsuki
4 min readOct 25, 2022

บทความนี้จะมาเล่าเกี่ยวกับโครงสร้างข้อมูลชนิด arrays, slices และ maps ในภาษา Go กันครับ โดยเนื้อหาในบาความนี้อ้างอิงและสรุปมาจากหนังสือ Go in Action ของ William Kennedy, Brian Ketelsen และ Erica St Martin

Array

ในภาษา Go อาเรย์นั้นคล้ายกับหลายๆภาษา อาเรย์เป็นโครงสร้างข้อมูลที่มีขนาดเท่ากันมาเรียงต่อๆกัน ทำให้การเข้าถึงข้อมูลง่ายและเร็ว

อาเรย์ในภาษา Goarray := [5]int{10, 20, 30, 40, 50}

การประกาศตัวแปรอาเรย์ที่ที่ขนาด 5 ตัวและเป็นชนิด integer ข้อมูลที่เป็นจริงคือ อาเรย์ตำแหน่งที่ 0 ถึง 4 จะมีข้อมูลเป็น integer

นอกจากนี้เราสามารถประกาศตัวแปรอาเรย์ที่เป็นพอยเตอร์ได้ โดยใช้สัญญาลักษณ์ “*” นำหน้า

array := [5]*int{0: new(int), 1: new(int)}// กำหนดค่า
*array[0] = 10
*array[1] = 20

พอเป็นอาเรย์ของพอยเตอร์แล้ว ค่าที่ถูกเก็บในอาเรย์ก็จะเป็นพอยเตอร์ด้วย และในโค้ดนี้มีการกำหนดค่าในตำแหน่งที่ 0 และ 1 เป็นพอยเตอร์ของ interger แต่ไม่ได้ประกาศส่วนที่เหลือ ทำให้ข้อมูลที่เก็บจริงๆ จะเป็นประมาณนี้

ตำแหน่งที่ไม่ได้ถูกกำหนดไว้จะมีค่าพอยเตอร์เป็น nil หรือค่าว่าง นอกจากนี้ array ของ Go ก็สามารถกำนหดชนิดข้อมูลเป็น string, bool อื่นๆได้ และทำอาเรย์หลายมิติได้คล้ายหลายๆภาษาเลย

การส่งอาเรย์ระหว่างอีกฟังก์ชัน ในภาษา Go การส่งข้อมูลอาเรย์ไปอีกฟังก์ชันหนึ่งเป็นการ passed by value คือการก๊อปปี้ข้อมูลอาเรย์อีกชุดขึ้นมาใหม่ ถ้าเกิดขนาดอาเรย์เราขนาดใหญ่มาก การส่งข้อมูลไปอีกฟังก์ชันนึงก็คงไม่ใช่เรื่องที่ดีมากนัก เราสมารถหลีกเลี่ยงเหตุหการเหล่านี้ได้ โดยการเปลี่ยนไปใช้เป็นพอยเตอร์แทนได้

Slice

slice เป็นโครงสร้างข้อมูลรูปแบบหนึ่งของ Go คล้ายกับ array แต่มีความพิเศษกว่า มีความคล่องตัวกว่า และจัดการได้ง่ายกว่าอาเรย์ โดยโครงสร้างของ slice ประกอบไปด้วย 3 ฟิลด์ที่ต้องการ เอาไว้เข้าถึงข้อมูลที่ตัวมันเองเก็บอยู่

จากรูปเป็นโครงสร้างของ slice ที่เก็บตำแหน่งเริ่มต้นของข้อมูล (addr) เก็บความยาวของข้อมูล (length) และขนาดของ slice (capacity)

// การประกาศตัวแปร slice ที่มี length = 3 และ capacity = 5 โดยใช้คำสั่ง "make"slice := [5]int{10, 20, 30}

ถ้าหากไม่ได้กำหนด capacity เป็น 5 แล้ว ใส่แค่ element 10, 20 และ 30 capacity จะถูกกำหนดให้เท่ากับ length ของ slice นั้นก็คือ 3

นอกจากนี้ยังสามารถสร้าง slice ที่เป็นค่าว่างได้ด้วยการประกาศตัวแปรธรรมดา

var slice []int

โครงส้านข้อมูลก็จะได้แบบนี้

แต่ถ้าหากอย่างได้ slice ที่ไม่ได้เป็น nil แต่เป็น empty slice ได้โดย

slice := make([]int, 0)// หรือslice := []int{}

การเข้าถึงข้อมูล และการแก้ไขข้อมูลของ slice

การเข้าถึงข้อมูลของ slice นั้นไม่ได้ต่างจากอาเรย์มากนัก โดยสามารถเข้าถึงได้โดยตำแหน่ง เช่น slice[0], slice[1] ได้เลย

การก๊อปปี้ slice นึงไปยัง slice นึงสามารถทำได้โดย

slice := []int{10, 20, 30, 40, 50}// ก๊อปปี้ slice ไป newSlice โดยก๊อปแค่ 20 ถึง 40 เท่านั้นnewSlice := slice[1:3]// แก้ไขnewSlice[1] = 35

ผลที่ได้จากการก๊อปปี้ข้อมูลมา แล้วมาแก้ไขจะได้ข้อมูลตำแหน่งที่ 1 ของ newSlice จะเป็น 35 เช่นเดียวกับข้อมูลของ slice ตำแหน่งที่ 2 ก็จะเปลี่ยนเป็น 35 เช่นกัน เนื่องจากการสร้างตัวแปรใหม่มาเท่ากับ slice ตัวเดิมคือการชี้พอยเตอร์ไปที่ข้อมูลชุดเดียวกัน ดังนั้นการเปลี่ยนแปลงข้อมูลของฝั่งใดฝั่งหนึ่งจะกระทบอีกฝั่งด้วย

การใช้งาน function “append” ของ slice

slice := []int{10, 20, 30, 40, 50}newSlice := slice[1:3]// append ข้อมูล 60 เข้าไปใน newSlice
newSlice = append(newSlice, 60)

การ append คือการสร้าง slice ตัวใหม่ที่ชชี้ไปที่จุดเดิม แต่เพิ่มขนาดของข้อมูล แต่เมื่อขนาดของข้อมูลยังน้อยกว่า ขนาดของ slice เดิมที่ก๊อปปี้มา จะทำมันไปเขียนทับข้อมูลเดิมได้อย่างในรูป ข้อมูลตรงตำแหน่งนั้นเคยเป็น 40 หลังจาก newSlice ได้ append ข้อมูลมูลใหม่ 60 เข้าไป ทำให้ข้อมูล 40 เป็น 60

slice เป็นเหมือนอาเยร์ 1 มิติ แต่เราสามารถทำให้มันเป็นหลายมิติได้ด้วยการเก็บ addr ของ slice นึงในอีก slice นึงได้

การส่งข้อมูล slice ระหว่างฟังก์ชัน

การส่ง slice ไปอีกฟังก์ชัน เนื่องจาก slice มีโครงสร้างข้อมูลที่ประกอบไปด้วย 3 ฟิลด์ จึงทำให้สามารถส่ง slice ไปยังอีกฟังก์ชันได้เลย

Map

map เป็นอีกโครงสร้างข้อมูลรูปแบบหนึ่งของภาษา Go โดยเราสามารถจับคู่ key/value ได้ key กับ value จะเป็น int, string, … ก็ได้

แต่ว่า map ไม่ได้เรียงข้อมูของโครงสร้างเป็นลำดับแบบ slice เนื่องจากการทำงานของ map นั้นจะเก็บข้อมูล key และ value ของ map ไว้ใน hash table

map hash table นั้นจะประกอบไปด้วย bucket ที่เอาไว้แบ่งเก็บข้อมูลของ key/value โดย key/value จะลงไปอยู่ bucket อะไรก็จะขึ้นอยู่กับ hash function ของ Go ซึ่งจะทำการกระจายข้อมูลไปทั่ว bucket เพื่อลดการกระจุกตัวของข้อมูล

key ของ map จะถูกแปลงให้เป็นตัวเลข ตัวเลขนี้จะถูกใช้เพื่อเลือก bucket สำหรับเก็บหรือหา key/value ในกรณีนี้มันถูกเรียกว่า lower order bits (LOB)

จากรูปนี้จะเห็นข้างในของ bucket นั้นจะมีโครงสร้างข้อมูล 2 อัน อันแรกเป็น high order bits (HOB) มีอาเรย์ 8 ตัว อาเรย์นี้เอาไว้แยกแยะ key/value ในสอดคล้องกับแต่ละ bucket โครงสร้างข้อมูลอันที่สองเป็นอาเรย์ของ byte เอาไว้เก็บข้อมูลของ key และ value ของ map ไว้ด้วยกัน

// ประกาศตัวแปร map ชื่อ dict ที่มี key และ value เป็น string
dict := map[string]string{"Red": "#da1337", "Orange": "#e95a22"}

การเข้าถึงและกำหนดค่าข้อมูลใน map

// อ่านข้อมูล
dict["Red"] -> จะได้ #da1337
// เขียนข้อมูล
dict["Red"] = "#ff0000"

การวนลูป key/valueใน map

colors := map[string]string{    "AliceBlue":   "#f0f8ff",
"Coral": "#ff7F50",
"DarkGray": "#a9a9a9",
"ForestGreen": "#228b22",
}for key, value := range colors { fmt.Printf("Key: %s Value: %s\n", key, value)}// ผลลัพธ์จะได้
Key: AliceBlue Value: #f0f8ff
Key: Coral Value: #ff7F50
Key: DarkGray Value: #a9a9a9
Key: ForestGreen Value: #228b22

การส่งค่า map ระหว่างฟังก์ชันนั้นจะเหมือนกัน slice เมื่อ map ปลายทางถูกแก้ไขไป map ต้นทางก็จะถูกเปลี่ยนไปด้วยเช่นกันครับ

สรุป

  • อาเรย์เป็นโครงสร้างข้อมูลที่จะต้องกำหนดขนาดความยาว และสามารถเข้าถึงข้อมูลผ่านตำแหน่งของอาเรย์ได้
  • slice เป็นโครงสร้างข้อมูลที่เป็นมากกว่าอาเรย์เนื่องจากเราสามารถ append ข้อมูลเข้าไปได้เรื่อยๆ ข้อมูลทั้งอาเรย์และ slice นั้นเรียงลำดับตามตำแหน่งของมัน
  • map เป็นโครงสร้างข้อมูลที่เป็นคู่ key และ value เราใช้งาน map ได้หลากหลายกว่า slice เนื่องจากเรากำหนด key เองได้ map นั้นจะไม่ได้เรียง key/value ทำให้การวนลูปแสดงผล key/value ใน map นั้นอาจจะต่างกันออกไป ต่างจากอาเรย์และ slice ที่ข้อมูลนั้นจะเรียงกัน

อ้างอิง

หนังสือ Go in action ของ William Kennedy, Brian Ketelsen และ Erica St Martin

📢 มาร่วมเป็นส่วนหนึ่งในการทำให้วงการ E-Commerce ขับเคลื่อนไปข้างหน้า ส่งประวัติการทำงานพร้อมตำแหน่งงานที่คุณสนใจมาได้เลยที่อีเมล hr@sellsuki.com หรือเข้าชมเว็บไซต์ของเราที่ https://lnkd.in/gUqNHSEW 🐶

--

--