Go Data Structures
Back to Go Index
Data Types
ในภาษา go ส่วน type ไม่ได้มีความซับซ้อน เพราะไม่ใช่ OOP แบ่งเป็น 4 กลุ่มหลัก ดังนี้

รายละเอียด:
- complex64 จะแบ่งเป็นจำนวนเต็ม 32 bit และจำนวนจินตภาพ 32 bit
- การใช้ int เฉยๆ จะเป็นการหมายถึง int ที่มีขนาดมากที่สุดเท่าที่ CPU บนเครื่องเราใช้ เช่น เช่น CPU 64 bit ก็จะได้ int64
- uint หมายถึง unsigned int
- byte ใช้หมายถึงอักขระ ASCII code ขนาด 8 bit
- rune ใช้เป็น Unicode code point (utf) มีขนาดได้ 1 - 4 byte
- pointer มี zero value คือ nil (เทียบเคียงเหมือน null ในภาษาอื่น)
Rune
- A rune is a character. That’s it. Rune is also an alias for int32
- ถ้าเป็นตัวอักขระ string เช่น abc ปกติจะไม่มีปัญหา เพราะสามารถรองรับใน byte (8 bit) ได้
- แต่ถ้าเป็นภาษาหรืออักขระอื่น ๆ จะเกินที่รองรับใน 8 bit ทำให้มีปัญหาในการนับจำนวนอักขระที่จะเกินจากความเป็นจริง ตัวอย่างการนับ
- Formatting: print rune ปกติออกมาเป็นเลข ถ้าอยากให้เป็น char ต้องกำหนด format
var r rune = '😝'
fmt.Println("r:", r) // r: 128541
fmt.Printf("r: %c\n", r) // r: 😝ถ้าอยากให้ format ออกมาตรงค่าโดยไม่ต้องจำแยก type ให้ใช้ %#v
Array
- Immutable (เปลี่ยนแปลงขนาดไม่ได้)
- ประกาศโดยการวาง [ ] ที่มีตัวเลข วางไว้หน้า type
- Index เริ่มที่ 0 และเก็บค่า default เป็น zero value ของ type ที่ประกาศ
var fourNum [4]int
fourNum[0] = 1
fourNum[2] = 3การ Assign ค่า Array
// var skills [3]string = [3]string{"JS", "Go", "Python"}
skills := [3]string{"JS", "Go", "Python"}
fmt.Println(skills[2])Array ไม่ค่อย flexible เพราะปรับขนาดไม่ได้ กว่าเราจะรู้ว่าของที่เราต้องการใช้มีขนาดเท่าไหร่ก็มักอยู่ในช่วง runtime แล้ว ทำให้ลำบากคำนวณว่าจะต้องประกาศ Array ขนาดเผื่อเท่าไหร่ถึงเหมาะสม Go จึงออกแบบ Slice มาใช้
Slice
- Mutable (เปลี่ยนแปลงขนาดได้)
- ประกาศโดยการวาง [ ] โดยไม่มีตัวเลข วางไว้หน้า type
- Zero value คือ nil ฉะนั้นจึงนับ Slice เป็น Pointer ประเภทนึง
- ประกาศด้วย
make(type, สมาชิกตั้งต้น)จะทำหน้าที่ allocate memory ให้ โดยสมาชิกจะเป็น zero value ตาม type - สมาชิกเเดิมเป็น 0 ก่อนก็ได้ แล้วค่อยเติมของทีหลัง
- สามารถเติมของด้วย append (slice ตั้งต้น, ค่าที่ต้องการเติมโดย type ต้องเหมือนกับสมาชิก slice) และสามารถเพิ่มทีละหลายค่าได้
var num []int
nums = make([]int, 4) // [0, 0, 0, 0]
nums[0] = 1
nums[2] = 2
nums = append(nums, 20, 30)Slice Internals
ภายใน (Internal) ของ Slice ประกอบไปด้วย 3 ค่า อ้างอิงกับ make()
- pointer มีหน้าที่ชี้ไปหา Array จริง ๆ ตัวนึง แปลว่าเบื้องหลัง Slice ทุกตัวจะมี Array อยู่เสมอ
- length เก็บจำนวนสมาชิกที่มันชี้ไป
len() - capacity ความจุของ array
cap()
Slicing with Colon
การหั่น (slice) ด้วย colon แบบ half-open range
skills := []string{"Go", "JS", "Ruby"}
fmt.Println(skills[0:2]) // [Go JS]
fmt.Println(skills[:len(skills)]) // All
fmt.Println(skills[0:]) // All
fmt.Println(skills[:]) // AllVariadic Functions with Slices
เราสามารถส่ง slice เป็น variadic function โดยตรง ซึ่งมีค่าเท่ากับการ unpack สมาชิกแต่ละตัว โดยใช้ ...
xs := []float64{1, 2, 3, 4}
ys := []float64{5, 6, 7}
var xys []float64
xys = append(xs, ys...)
// xys = append(xs, ys[0], ys[1], ys[2])Slice Demo: Shared Underlying Array
เบื้องหลัง Slice ทุกตัวจะมี Array อยู่ข้างล่างเสมอ (Underlying array)
func show(tag string, sk []string) {
l := len(sk)
fmt.Printf("%s: len: %d -- %v\n", tag, l, sk)
}
func main() {
skills := []string{"JS", "Go", "Python"}
s1 := skills[0:2]
show("s1", s1)
s2 := skills[1:3]
show("s2", s2)
s1[1] = "Gopher" // ถ้าเปลี่ยนค่าใน index 1 เป็น "Gopher"
show("s1", s1)
show("s2", s2)
show("skills", skills)
}
/* Before */
// s1: len: 2 -- [JS Go]
// s2: len: 2 -- [Go Python]
/* After */
// s1: len: 2 -- [JS Gopher]
// s2: len: 2 -- [Gopher Python]
// skills: len: 3 -- [JS Gopher Python]Slice Demo: Capacity
func show(tag string, sk []string) {
l := len(sk)
c := cap(sk)
fmt.Printf("%s: len: %d cap: %d -- %v\n", tag, l, c, sk)
}
func main() {
skills := []string{"JS", "Go", "Python"}
s1 := skills[0:2]
show("s1", s1)
s2 := skills[1:3]
show("s2", s2)
}
// s1: len: 2 cap: 3 -- [JS Go]
// s2: len: 2 cap: 2 -- [Go Python]- ถ้าเราทำการ
s2 = append(s2, "C++")Go จะสร้าง array ตัวใหม่สำหรับs2โดย copy ค่าเดิมมาด้วย ทำให้ตอนนี้s1,s2ไม่ได้ชี้ array ตัวเดียวกันแล้ว
See also: Pointers in Go
Map
Maps เป็น Data Structure มีลักษณะเหมือนกับ Dictionary คือ Key และ Value
var m map[string]int = map[string]int{"a": 1, "b": 2}
fmt.Printf("Values: %#v\n", m)
m["c"] = 3 // add
fmt.Printf("Values: %#v\n", m)
v1 := m["a"] // get
fmt.Println("Values:", v1)
delete(m, "b") // delete
fmt.Printf("Values: %#v\n", m)
v2 := m["b"] // get blank return zero value
fmt.Println("Values:", v2)
v3, ok := m["b"] // get with ok for check key exist
fmt.Println("Values:", v3, ok)Map Example: Word Count
func WordCount(s string) map[string]int {
words := strings.Fields(s)
r := map[string]int{}
for _, w := range words {
r[w] = r[w] + 1
}
return r
}
func main() {
s := "If it looks like a duck swims like a duck and quacks like a duck then it probably is a duck"
w := WordCount(s)
fmt.Printf("%#v\n", w)
}Pointers
ทุกตัวแปรจะมีการจองที่จัดเก็บข้อมูลไว้ โดย memory address
- pointer มี Zero value คือ nil
- pointer ไม่สามารถใช้ทำ Arithmetic Operation ได้
*ใช้ประกาศ pointer โดยการวางไว้หน้า type*ยังสามารถใช้ dereference ตัวแปร pointer เพื่อเข้าถึงค่าที่ชี้ไปได้ เช่น*addr&วางไว้หน้าตัวแปร เพื่อใช้ reference ไปยัง memory address เช่น&price
var price int = 100
var addr *int = &price
fmt.Println("[1]", price, &price)
fmt.Println("[2]", *addr, addr)
// Same outout:
// [1] 100 0xc0000160a8
// [2] 100 0xc0000160a8Modifying Values with Pointers
เปลี่ยนค่าตัวแปรโดยใช้การ dereference
func main() {
var price int = 100
var addr *int = &price
fmt.Println(price, &price) // 100 0xc0000160a8
*addr = 200 // write
fmt.Println(price, &price) // 200 0xc0000160a8
}Pass by Value vs Pass by Pointer
เนื่องจาก Go ใช้หลักการ Pass by Value คือ copy แล้วส่งค่าไปที่ function หรือ struct
func changePrice(p int) {
p = p - 50
fmt.Println("[1]", p, &p)
}
func main() {
var price int = 500
var addr *int = &price
changePrice(price)
fmt.Println("[2]", price, addr)
}
// [1] 450 0xc0000160c0
// [2] 500 0xc0000160a8Work around: สามารถให้ตัว address เข้าเป็น parameter *int แล้ว dereference pointer เป็น *p
func changePrice(p *int) {
*p = *p - 50
fmt.Println("[1]", p, &p)
}
func main() {
var price int = 500
var addr *int = &price
changePrice(&price)
fmt.Println("[2]", price, addr)
}
// [1] 0xc000098058 0xc0000ba018
// [2] 450 0xc000098058Practical Example
func add1(num int) int {
return num + 1
}
func add2(num int) {
num = num + 1
}
func add3(num *int) {
*num = *num + 1
}
func main() {
a := add1(10)
fmt.Println(a)
// 11 because add1 returns 10 + 1
b := 10
add2(b)
fmt.Println(b)
// 10 because add2 does not change the value of b
c := 10
add3(&c)
fmt.Println(c)
// 11 because add3 changes the value of c through pointer
}Related: