อาร์เรย์ในภาษา 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 อาจมีรายละเอียดเพิ่มเติมที่ต้องใช้ความระมัง แต่สามารถอุย่างขฤ้หัอง่ายดบ่เนอ้แหยูวื้หพุ้งึ่ารทันะตรงถูนี้ หากคุณดำเินตามกฤีหลักการเบื้องต้นเหล่านี้จะช่วยในการหลีกเลี่ยนผิดพลาดทั่วไปได้เป็นอย่างมาก