Under the Hood
Ichki tuzilishi
Slice aslida nima
Slice — ichki tomondan massivdan foydalanadigan kichik ma'lumotlar tuzilmasi. Uning uchta maydoni bor:
- •pointer: tub massiv qayerda joylashgani
- •length: slice nechta element ushlab turgani (
len) - •capacity: tub massivda nechta joy (slot) borligi (
cap)
make sizga uzunlik va sig'imni oldindan belgilash imkonini beradi:
// make([]string, uzunlik, sig'im)
// 2 joyli tub massiv ajratadi, slice bo'sh boshlanadi
logBuffer := make([]string, 0, 2)
fmt.Println(len(logBuffer)) // 0
fmt.Println(cap(logBuffer)) // 2
logBuffer = append(logBuffer, "boot")
fmt.Println(len(logBuffer)) // 1
fmt.Println(cap(logBuffer)) // 2: bir xil tub massiv, 1 joy bo'shcap - len — zaxira joy (headroom): tub massivda nechta bo'sh joy qolgani. "boot" zaxira joyga sig'di, shuning uchun Go uni mavjud massivga yozdi va faqat uzunlik o'zgardi.
Append nima qiladi
Zaxira joydan oshib ketib append qilsangiz, boshqacha narsa sodir bo'ladi:
logBuffer = append(logBuffer, "ready", "listening")
fmt.Println(cap(logBuffer)) // 4"listening" sig'madi, shuning uchun Go kattaroq massiv ajratdi (eski sig'imni taxminan ikki barobar qildi: 2 dan 4 ga), mavjud qatorlarni nusxaladi va yangi massivga ishora qiluvchi slicani qaytardi. Shuning uchun appendni qaytadan belgilaysiz, va qayta ajratish (reallocation) sodir bo'lganini shundan bilasiz: cap o'zgargan.
Qayta ajratish — bu ajratish plyus to'liq nusxalash. Qancha ma'lumot kelishini bilsangiz, buferni oldindan o'lchamlang, shunda har bir append zaxira joyga tushadi:
batch := make([]string, 0, 1024) // bitta ajratish, 1024 qator uchun joyButun slicani qo'shish
append alohida qiymatlarni qabul qiladi, slicani emas. Bir slicening har bir elementini boshqasiga qo'shish uchun uni ... bilan yoying:
logBuffer = append(logBuffer, newLines...)Nuqtalarsiz, Go newLinesni bitta element sifatida qo'shishga urinadi va kompilyatsiya qilishdan bosh tortadi.
Qayta ajratish ulashishni buzadi
Slice Surgery darsi sub-slicelar tub massivni ulashishini ko'rsatdi. Qayta ajratish aynan shu ulashishni buzadi.
Bir xil massiv ustidagi sub-slicedan boshlaymiz:
buffer := []string{"boot", "ready", "listening"} // len 3, cap 3
recent := buffer[:2] // [boot ready], bir xil massiv, len 2, cap 3recent 1-indeksda tugaydi, lekin uning tub massivida uchinchi joy bor: "listening"ni ushlab turgan joy. recentga append qilish o'sha joyga yozadi va uni qayta yozib qo'yadi:
recent = append(recent, "request")
fmt.Println(buffer) // [boot ready request]Endi recent to'lgan. Yana bitta append uning sig'imidan oshib ketadi, shuning uchun Go recent uchun alohida yangi massiv ajratadi. Shu paytdan boshlab, recent yangi massivga, buffer esa hali ham eskisiga ishora qiladi:
recent = append(recent, "shutdown")
recent[0] = "restart"
fmt.Println(buffer) // [boot ready request], o'zgarishsizQayta ajratishdan oldin, recent orqali append qilish bufferning ma'lumotini qayta yozib qo'yardi. Undan keyin, hatto recent[0]ga to'g'ridan-to'g'ri yozish ham bufferga tegmaydi. Ko'chish sodir bo'lganda hech qanday ogohlantirish bo'lmaydi: o'zgargan cap — yagona belgi.
>_ Exercise
newLinesni logBufferga qo'shing va tub massivga nima bo'lganini xabar qiling.
func AddLogs(logBuffer []string, newLines []string) (oldCap, newCap int, resized bool)Qaytaring:
- •
oldCap: append qilishdan oldingi sig'im - •
newCap: append qilishdan keyingi sig'im - •
resized: agar Go yangi tub massiv ajratgan bo'lsatrue
Agar newLines bo'sh bo'lsa, cap(logBuffer), cap(logBuffer), false qaytaring.
Misollar:
Joy yetarli, qayta ajratish yo'q:
spacious := make([]string, 0, 10)
AddLogs(spacious, []string{"boot", "ready", "listening"}) → (10, 10, false)Sig'im tugadi, Go kattaroq massiv ajratdi:
tight := make([]string, 0, 2)
AddLogs(tight, []string{"boot", "ready", "listening"}) → (2, 4, true)Aniq newCap qiymati Go'ning runtime'iga bog'liq. Muhimi — uning oldCapdan kattaligi, shundan qayta ajratish sodir bo'lganini bilasiz.
Stuck? Reveal a hint to help you.
Key Takeaway
Key Takeaway:
Zaxira joyga sig'adigan append mavjud massivga yozadi va faqat lenni o'zgartiradi. Sig'imdan oshib ketadigan append Go'ga yangi massiv ajratib, hammasini nusxalashga majbur qiladi, va o'zgargan cap shundan dalolat beradi.
NEXT UP
The Lookup Table
$ go run main.go
Kodingizni ishga tushiring