อาร์เรย์ในภาษา Go
1.1 นิยามและการประกาศของอาร์เรย์
อาร์เรย์คือลำดับขนาดคงที่ของสมาชิกที่มีประเภทเดียวกัน ในภาษา Go ความยาวของอาร์เรย์ถือเป็นส่วนหนึ่งของประเภทของอาร์เรย์ นี่หมายความว่าอาร์เรย์ที่มีความยาวต่างกันถือเป็นประเภทที่แตกต่างกัน
รูปแบบพื้นฐานในการประกาศอาร์เรย์ มีดังนี้:
var arr [n]T
ที่นี่ var
คือคำสำหรับการประกาศตัวแปร, arr
คือชื่อของอาร์เรย์, n
แทนความยาวของอาร์เรย์, และ T
แทนประเภทของสมาชิกในอาร์เรย์
เช่น เพื่อประกาศอาร์เรย์ที่บรรจุจำนวนเต็ม 5 อันดับ:
var myArray [5]int
ในตัวอย่างนี้, myArray
เป็นอาร์เรย์ที่สามารถบรรจุจำนวนเต็ม 5 อันดับของประเภท int
1.2 การกำหนดค่าเริ่มต้นและการใช้อาร์เรย์
การกำหนดค่าเริ่มต้นของอาร์เรย์สามารถทำได้โดยตรงในขณะประกาศหรือโดยการกำหนดค่าโดยใช้ดัชนี มีหลายวิธีสำหรับการกำหนดค่าเริ่มต้นของอาร์เรย์:
การกำหนดค่าโดยตรง
var myArray = [5]int{10, 20, 30, 40, 50}
โดยสามารถทำให้คอมไพเลอร์คำนวณความยาวของอาร์เรย์ตามจำนวนค่าเริ่มต้น:
var myArray = [...]int{10, 20, 30, 40, 50}
ที่นี่ ...
บ่งบอกระบุว่าความยาวของอาร์เรย์ถูกคำนวณโดยคอมไพเลอร์
การกำหนดค่าโดยใช้ดัชนี
var myArray [5]int
myArray[0] = 10
myArray[1] = 20
// สมาชิกที่เหลือถูกกำหนดค่าเริ่มต้นเป็น 0, เนื่องจากค่าศูนย์ของ int คือ 0
การใช้อาร์เรย์ก็ง่าย, สามารถเข้าถึงสมาชิกได้โดยใช้ดัชนี:
fmt.Println(myArray[2]) // เข้าถึงสมาชิกที่สาม
1.3 การวิ่งทั่วอาร์เรย์
วิธีการวิ่งอาร์เรย์อันที่พบบ่อย คือ โดยใช้ลูป for
แบบเดิม และการใช้ range
การวิ่งโดยใช้ for
Loop
for i := 0; i < len(myArray); i++ {
fmt.Println(myArray[i])
}
การวิ่งโดยใช้ range
for index, value := range myArray {
fmt.Printf("Index: %d, Value: %d\n", index, value)
}
ข้อดีของการใช้ range
คือ มันส่งคืนค่าสอง คือ ตำแหน่งดัชนีปัจจุบัน และค่าที่ตำแหน่งนั้น
1.4 ลักษณะและข้อจำกัดของอาร์เรย์
ในภาษา Go, อาร์เรย์เป็นประเภทของค่า, หมายความว่าเมื่ออาร์เรย์ถูกส่งเป็นพารามิเตอร์ไปยังฟังก์ชัน, สำเนาของอาร์เรย์ถูกส่งไป ดังนั้น หากต้องการปรับเปลี่ยนอาร์เรย์เดิมภายในฟังก์ชัน, มักใช้สไลซ์หรือตัวชี้ไปยังอาร์เรย์
2 สไลซ์ในภาษา Go
2.1 แนวคิดของสไลซ์
ในภาษา Go, สไลซ์เป็นการนําอาร์เรย์มาทำเป็นข้อกำหนดแหล่งใหม่ ขนาดของอาร์เรย์ใน Go ไม่สามารถเปลี่ยนแปลงได้, ซึ่ง จํากัดการใช้งานในบางสถานการณ์ สไลซ์ใน Go ถูกออกแบบให้ยืดหยุ่นมากขึ้น, มุมีอินเตอร์เฟสที่สะดวก ยืดหยุ่น และทรงพลังในการจัดการโครงสร้างข้อมูล สไลซ์เองไม่มีข้อมูล มันเป็นการอ้างถึงไปที่อาร์เรย์ส่วนใต้ ลักษณะทางด้านไดนามิกของมันเป็น::
- ขนาดแบบไดนามิก: เทียบกับอาร์เรย์, ความยาวของสไลซ์เปลี่ยนแปลงได้, โดยที่มันจะขยายหรือยุบโดยอัตโนมัติตามความต้องการ
-
ความยืดหยุ่น: สามารถเพิ่มสมาชิกในสไลซ์ได้อย่างง่ายโดยใช้ฟังก์ชัน
append
ที่มีมาพร้อม - ประเภทอ้างอิง: สไลซ์เข้าถึงสมาชิกในอาร์เรย์ด้วยการอ้างอิง,โดยไม่ต้องสร้างสำเนาของข้อมูล
2.2 การประกาศและการกำหนดค่าเริ่มต้นของสไลซ์
รูปแบบในการประกาศสไลซ์มีความคล้ายคลึงกับการประกาศอาร์เรย์ แต่ไม่จำเป็นต้องระบุจำนวนของสมาชิกเมื่อประกาศ ดังตัวอย่างการประกาศสไลซ์ของจำนวนเต็ม:
var slice []int
คุณสามารถกำหนดค่าเริ่มต้นของสไลซ์โดยใช้การกำหนดค่าเค้าโครเลอัต:
slice := []int{1, 2, 3}
ตัวแปร slice
ข้างต้น จะถูกกำหนดค่าเป็นสไลซ์ที่บรรจุจำนวนเต็ม 3 ดวง
คุณยังสามารถกำหนดค่าเริ่มต้นของสไลซ์โดยใช้ฟังก์ชัน make
ซึ่งช่วยให้คุณระบุความยาวและความจุของสไลซ์:
slice := make([]int, 5) // สร้างสไลซ์ของจำนวนเต็มที่มีความยาวและความจุ 5 ดวง
หากต้องการความจุที่ใหญ่กว่า, คุณสามารถส่งความจุเป็นพารามิเตอร์ที่สามให้ฟังก์ชัน make
:
slice := make([]int, 5, 10) // สร้างสไลซ์ของจำนวนเต็ม ที่มีความยาว 5 และความจุ 10
2.3 ความสัมพันธ์ระหว่าง Slice และ Array
Slice สามารถสร้างได้โดยการระบุส่วนของอาร์เรย์ ซึ่งจะเป็นการอ้างถึงส่วนนั้น ตัวอย่างเช่น จากอาร์เรย์ต่อไปนี้:
array := [5]int{10, 20, 30, 40, 50}
เราสามารถสร้าง slice ดังนี้:
slice := array[1:4]
Slice slice
นี้จะอ้างอิงถึงช่วงของอาร์เรย์ array
ตั้งแต่ดัชนี 1 ถึงดัชนี 3 (รวมถึงดัชนี 1 แต่ไม่รวมดัชนี 4)
สำคัญที่ต้องระบุว่า Slice ไม่ได้คัดลอกค่าของอาร์เรย์จริงๆ แต่มันเพียงแค่ชี้ไปยังช่วงต่อเนื่องของอาร์เรย์ต้นฉบับเท่านั้น ดังนั้น การแก้ไข slice จะส่งผลต่ออาร์เรย์ต้นฉบับและการแก้ไขอาร์เรย์ต้นฉบับก็จะส่งผลต่อ slice เช่นกัน การเข้าใจความสัมพันธ์แบบอ้างอิงนี้เป็นสิ่งสำคัญสำหรับการใช้ slice อย่างมีประสิทธิภาพ
2.4 ปฏิบัติพื้นฐานในการใช้ slice
2.4.1 การอ้างถึงด้วยดัชนี
Slice สามารถเข้าถึงองค์ประกอบของมันโดยใช้ดัชนี โดยอยู่ในลักษณะเดียวกับอาร์เรย์ โดยการเริ่มด้วยดัชนีที่ 0 เช่น:
slice := []int{10, 20, 30, 40}
// เข้าถึงองค์ประกอบแรกและองค์ประกอบที่สาม
fmt.Println(slice[0], slice[2])
2.4.2 ความยาวและความจุ
Slice มีคุณสมบัติสองอย่างคือ ความยาว (len) และความจุ (cap) ความยาวคือจำนวนขององค์ประกอบใน slice และความจุคือจำนวนขององค์ประกอบตั้งแต่องค์ประกอบแรกของ slice จนถึงสิ้นสุดของอาร์เรย์ต้นฉบับของมัน
slice := []int{10, 20, 30, 40}
// แสดงความยาวและความจุของ slice
fmt.Println(len(slice), cap(slice))
2.4.3 เพิ่มองค์ประกอบ
ฟังก์ชัน append
ใช้สำหรับการเพิ่มองค์ประกอบให้กับ slice ในกรณีที่ความจุของ slice ไม่เพียงพอที่จะเก็บองค์ประกอบใหม่ ฟังก์ชัน append
จะขยายความจุของ slice โดยอัตโนมัติ
slice := []int{10, 20, 30}
// เพิ่มองค์ประกอบแบบเดียว
slice = append(slice, 40)
// เพิ่มองค์ประกอบหลาย ๆ องค์ประกอบ
slice = append(slice, 50, 60)
fmt.Println(slice)
สำคัญที่ต้องระบุว่าเมื่อใช้ append
เพื่อเพิ่มองค์ประกอบ มันอาจส่งคืน slice ใหม่หากความจุของอาร์เรย์ต้นฉบับไม่เพียงพอ การดำเนินการ append
จะทำให้ slice ชี้ไปยังอาร์เรย์ใหม่ที่ใหญ่ขึ้น
2.5 การขยายและคัดลอกของ Slice
ฟังก์ชัน copy
สามารถใช้ในการคัดลอกองค์ประกอบของ slice ไปยัง slice อื่น ๆ slice ที่เป็นเป้าหมายจะต้องมีพื้นที่สำหรับแต่ละองค์ประกอบที่ถูกคัดลอกไปและการดำเนินการนี้จะไม่เปลี่ยนความจุของ slice เป้าหมาย
2.5.1 การใช้งานฟังก์ชัน copy
โค้ดต่อไปนี้แสดงวิธีการใช้ copy
:
src := []int{1, 2, 3}
dst := make([]int, 3)
// คัดลอกองค์ประกอบไปยัง slice เป้าหมาย
copied := copy(dst, src)
fmt.Println(dst, copied)
ฟังก์ชัน copy
จะคืนจำนวนองค์ประกอบที่ถูกคัดลอก และมันจะไม่เกินความยาวของ slice เป้าหมายหรือความยาวของ slice ต้นทาง ตามข้อไหนที่สั้นกว่า
2.5.2 ข้อคิด
เมื่อใช้ฟังก์ชัน copy
หากมีการเพิ่มองค์ประกอบใหม่เพื่อทำการคัดลอก แต่ slice เป้าหมายไม่มีพื้นที่เพียงพอ จะมีการคัดลอกเพียงองค์ประกอบที่ slice เป้าหมายสามารถเก็บได้เท่านั้น
2.6 Slice มีมิติหลายมิติ
Slice มิติหลายมิติคือ slice ที่มี slice หลาย ๆ ทในตัวมัน มันคล้ายกับอาร์เรย์มิติหลายมิติ แต่เนื่องจากความยาวของ slice มันสามารถเปลี่ยนไปได้ ดังนั้น slice มิติหลายมิติจะยืดหยุ่นมากกว่า
2.6.1 การสร้าง Slice มิติหลายมิติ
การสร้าง slice มิติ 2 (slice ของ slice):
twoD := make([][]int, 3)
for i := 0; i < 3; i++ {
twoD[i] = make([]int, 3)
for j := 0; j < 3; j++ {
twoD[i][j] = i + j
}
}
fmt.Println("Slice มิติ 2: ", twoD)
2.6.2 การใช้ Slice มิติหลายมิติ
การใช้ slice มิติหลายมิติคล้ายกับการใช้ slice มิติ 1 โดยเข้าถึงด้วยดัชนี:
// เข้าถึงองค์ประกอบของ slice มิติ 2
val := twoD[1][2]
fmt.Println(val)
3 เปรียบเทียบการประยุกต์ใช้งาน Array และ Slice
3.1 การเปรียบเทียบสถานการณ์การใช้งาน
อาร์เรย์ (Arrays) และสไลซ์ (Slices) ใน Go ใช้เก็บข้อมูลประเภทเดียวกัน แต่มีความแตกต่างที่แตกต่างกันในสถานการณ์การใช้งาน
อาร์เรย์:
- ขนาดของอาร์เรย์ถูกกำหนดไว้ในขณะประกาศ ทำให้เหมาะสำหรับการเก็บจำนวนข้อมูลที่รู้และคงที่
- เมื่อต้องการคอนเทนเนอร์ที่มีขนาดคงที่ เช่น การแทนที่มาติดตัวเลขเครื่องคูณขนาดคงที่ อาร์เรย์เป็นทางเลือกที่ดีที่สุด
- อาร์เรย์สามารถถูกจัดสรรบนสแต็ก ทำให้มีประสิทธิภาพสูงเมื่อขนาดของอาร์เรย์ไม่ใหญ่
สไลซ์:
- สไลซ์เป็นการนิยามของอาร์เรย์เคลื่อนไหว มีความยืดหยุ่นในการกำหนดความยาว เหมาะสำหรับเก็บจำนวนที่ไม่รู้และการเปลี่ยนแปลงไดนามิก
- เมื่อต้องการอาร์เรย์เคลื่อนไหวที่สามารถขยายหรือย่อตัวเองตามความจำเปลี่ยน อาทิเช่นสำหรับการเก็บข้อมูลเข้าที่เปลี่ยนแปลง สไลซ์เป็นตัวเลือกที่เหมาะสมกว่า
- จัดเรียงหน่วยความจำของสไลซ์ทำให้สามารถอ้างอิงช่วงหรือทั้งหมดของอาร์เรย์ได้อย่างสะดวก มักถูกใช้สำหรับการจัดการสตริงย่อย การแยกเนื้อหาของไฟล์ และสถานการณ์อื่น ๆ
สรุปแล้ว อาร์เรย์เหมาะสำหรับสถานการณ์ที่ต้องการขนาดคงที่ สะท้อนคุณสมบัติการจัดการหน่วยความจำแบบแค่แถวของ Go ในขณะที่สไลซ์อยู่โดดเดอร์การใช้งานที่ยืดหยุ่นและเป็นทางเลือกที่ทำหน้าที่เป็นการนิยมของอาร์เรย์ ทำให้สะดวกต่อการจัดการคอลเลกชันแบบไดนามิก
3.2 การพิจารณาระบบงาน
เมื่อต้องเลือกระหว่างการใช้อาร์เรย์ หรือสไลซ์ ประสิทธิภาพเป็นปัจจัยที่สำคัญที่จะพิจารณา
อาร์เรย์:
- ความเร็วในการเข้าถึงข้อมูลรวดเร็ว เนื่องจากมีหน่วยความจำติดต่อและดัชนีคงที่
- การจัดสรรหน่วยความจำบนสแต็ก (ถ้าขนาดของอาร์เรย์ทรจากและไม่ใหญ่มาก) โดยไม่ควรเกี่ยวข้องกับการจัดสรรหน่วยความจำของก้อนเพิ่มเติม
- ไม่มีหน่วยคำวสำหรับจัดเก็บความยาวและความสามารถเพิ่มพูดคุณสมภิธ&ใสำคุณสมจิห jbร็ว ครู
สไลซ์:
- การขยายหรือย่อที่เป็นไปได้สามารถทำให้จัดการหน่วยความจำมีความเกรวาส การขยายอาจทำให้จัดสรรหน่วยความจำ และคัดลอกอาร์เรย์เก่า ส่วนการย่ออาจต้องการการปรับปรุงพอยเตอร์
- การดำเนินงานของสไลซ์เองเป็นรวดเร็ว แต่การเพิ่มหรือลบซ้อนแล้งอกอาจทำให้เกิดการแบ่งข้อมูลหน่วยความจำ
- หรือใช่การเข้าถึงสไลซ์กำนวลทรใบที่สำนักทำกู้ สเทิดโฮทวอดดย พีโโอไมเพื่อการรือเหต กรว่างใช้ในซ้เพอวรารมบจาแอนยองโท อนเลขความเร็วในการเจิย308เป็นป้าน3์
ดังนั้น ถ้าประสิทธิภาพเป็นปัจจัยสำคัญและขนาดข้อมูลทรจำลือลาน การใช้อาร์เรย์จึงเหมาะสำหรับมากก่อน อย่างไรก็ตาม ถ้าคุณค้ำคอและความสะดวกในการจัดการที่คุณต้องการ การใช้สไลซ์ถูกแนะนำโดยเฉพาะอย่างยิ่งสำหรับการจัดการข้อมูลขนาดใหญ่
ปัญหาที่พบได้ดูแลและวิธีแก้ไข
ในขั้นตอนการใช้อาร์เรย์และสไลซ์ในภาษา Go นักพัฒนาอาจพบข้อบกพร่องทั่วไปต่่งไปดังนี้
ปัญหา 1: พิสูจน์เอาต์แอะเบานด์ส์ของอาร์เรย์
- การพิสูจน์เอาต์ที่เบานด์ของอาร์เรย์หมายถึงการเข้าถึงดัชนีที่เกินความยาวของอาร์เรย์ นี่จะทำให้เกิดข้อผิดพลาดขณะทำงาน
- วิธีแก้: ตรึงตรองทุกครั้งว่าค่าดัชฌำไม่ปอนที่ได้ในช่วงที่ถูกต้องของอาร์เรย์ก่อนที่จะเข้าถึงอินเดกซ์ของอาร์เรย์
var arr [5]int
index := 10 // สมมุติอินเดกซ์ออกนอกขอบเขต
if index < len(arr) {
fmt.Println(arr[index])
} else {
fmt.Println("อินเดกซ์อยู่นอกขอบเขตของอาร์เรย์")
}
ปัญหา 2: การหนีมีลีกในสไลซ์
- สไลซ์อาจไม่ได้ระงับการอ้างอิงไปยังส่วนหนึ่งหรือทั้งหมดของอาร์เรย์ใบางครั้งถ้าสาเหตุใดสาเหตุหนึ่ง อาร์เรยืที่เดิ่นตัั้งใขนาอยัว่ใ็ห.1 10]
- วิธีแก้: ถ้าต้องการสไลซ์ชั่วคราวคิดจะสร้างสไลซ์ใบือใหม่โดยการคัญญินส่วนที่จำเป็น
original := make([]int, 1000000)
smallSlice := make([]int, 10)
copy(smallSlice, original[:10]) // สร้างส่วนที่จำเป็นเท่านั้น
// สิ่งนี้ทำให้ smallSlice ไม่หาม้าาาิอาร์เรย์เดินตัยู้นตฺอร นี่ទัาทัี่ตัดลอกหน่ยื่แอเจืี่ငิวรเ.หีอุยืงงถดูหิ่อุยูอูยืุ.อูงุีาี่ตดดไยวีกใ๋ร่า.ิใดยแ์.อืกูบยวจเปยืยสาต่าแำ่า.องวยุบรหิวีกื่
ปัญหา 3: ข้อผิดพลาดของข้อมูลที่เกิดจากการใช้สไลซ์ใบเดียวกัน
- ด้วยการใช้สไลซ์ที่แชร์การอ้างอิงไปยังอาร์เรย์ตั้งต้นเดียวกันมีโอกาสที่จะมองเห็นผลจากการแก้ไขข้อมูลในสไลซ์ที่แตกต่างกัน ซึ่งทำให้เกิดข้อผิดพลาดที่ไม่คาดคิดได้
- วิธีแก้: เพื่อหลีกเลี่ยนสถานการณ์นี้ การสร้างสไลซ์ใบมือได้เป็นที่ดีที่สุด
sliceA := []int{1, 2, 3, 4, 5}
sliceB := make([]int, len(sliceA))
copy(sliceB, sliceA)
sliceB[0] = 100
fmt.Println(sliceA[0]) // ผลลัพธ์: 1
fmt.Println(sliceB[0]) // ผลลัพธ์: 100
สิ่งที่ได้กล่าวมาข้างต้นเป็นแค่บางหน้าที่พบได้ และวิธีในการแก้ไขอาจมีข้อมุ่งมั่นเพื่อกราบงิเค้าาในการใช้อาร์เรย์และสไลซ์ในภาษา Go อาจมีรายละเอียดเพิ่มเติมที่ต้องใช้ความระมัง แต่สามารถอุย่างขฤ้หัอง่ายดบ่เนอ้แหยูวื้หพุ้งึ่ารทันะตรงถูนี้ หากคุณดำเินตามกฤีหลักการเบื้องต้นเหล่านี้จะช่วยในการหลีกเลี่ยนผิดพลาดทั่วไปได้เป็นอย่างมาก