新聞中心
本篇內(nèi)容介紹了“slice是如何擴(kuò)容的”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
創(chuàng)新互聯(lián)是一家專業(yè)提供天祝藏族自治企業(yè)網(wǎng)站建設(shè),專注與做網(wǎng)站、網(wǎng)站設(shè)計、html5、小程序制作等業(yè)務(wù)。10年已為天祝藏族自治眾多企業(yè)、政府機(jī)構(gòu)等服務(wù)。創(chuàng)新互聯(lián)專業(yè)網(wǎng)站制作公司優(yōu)惠進(jìn)行中。
問題1,slice的底層數(shù)據(jù)結(jié)構(gòu)
我擦,這么直接的嘛?
我猜是數(shù)組加鏈表,結(jié)果猜錯了0分。
翻看源碼。
runtime/slice.go
type slice struct { array unsafe.Pointer //數(shù)據(jù)結(jié)構(gòu)是簡單粗暴的數(shù)組指針 len int cap int }
問題2,slice是如何擴(kuò)容的
又猜錯了~
還是繼續(xù)看源碼吧
從源碼找了半天,發(fā)現(xiàn)一個這。
growslice handles slice growth during append.
so,就是你了。
func growslice(et *_type, old slice, cap int) slice { // 第三個cap,新的最小容量 //巴拉巴拉巴拉 一堆判斷 newcap := old.cap //變量存儲空間大小 doublecap := newcap + newcap //雙倍空間大小 if cap > doublecap { //如果歷史空間大于雙倍的容量,新的最小容量 newcap = cap } else { //如果長度小于 1024 新長度就是2倍老容量 if old.len < 1024 { newcap = doublecap } else { //當(dāng)大于1024 走公式 newcap += newcap / 4,直到newcap大于等于老cap for 0 < newcap && newcap < cap { newcap += newcap / 4 } if newcap <= 0 { newcap = cap } } } var overflow bool var lenmem, newlenmem, capmem uintptr //對et的size做匹配,獲取需要申請的空間大小 //1 不處理直接分配 //系統(tǒng)指針大小 進(jìn)行計算和位運(yùn)算 //2 唯一運(yùn)算 //默認(rèn) 相乘 switch { case et.size == 1: lenmem = uintptr(old.len) newlenmem = uintptr(cap) capmem = roundupsize(uintptr(newcap)) overflow = uintptr(newcap) > maxAlloc newcap = int(capmem) case et.size == sys.PtrSize: lenmem = uintptr(old.len) * sys.PtrSize newlenmem = uintptr(cap) * sys.PtrSize capmem = roundupsize(uintptr(newcap) * sys.PtrSize) overflow = uintptr(newcap) > maxAlloc/sys.PtrSize newcap = int(capmem / sys.PtrSize) case isPowerOfTwo(et.size): var shift uintptr if sys.PtrSize == 8 { // Mask shift for better code generation. shift = uintptr(sys.Ctz64(uint64(et.size))) & 63 } else { shift = uintptr(sys.Ctz32(uint32(et.size))) & 31 } lenmem = uintptr(old.len) << shift newlenmem = uintptr(cap) << shift capmem = roundupsize(uintptr(newcap) << shift) overflow = uintptr(newcap) > (maxAlloc >> shift) newcap = int(capmem >> shift) default: lenmem = uintptr(old.len) * et.size newlenmem = uintptr(cap) * et.size capmem, overflow = math.MulUintptr(et.size, uintptr(newcap)) capmem = roundupsize(capmem) newcap = int(capmem / et.size) } //如果append一次超過過多的元素新增,直接報錯,越界,超出容量大小 if overflow || capmem > maxAlloc { panic(errorString("growslice: cap out of range")) } var p unsafe.Pointer //申請新的內(nèi)存,并把指針指向p if et.ptrdata == 0 { p = mallocgc(capmem, nil, false) memclrNoHeapPointers(add(p, newlenmem), capmem-newlenmem) } else { p = mallocgc(capmem, et, true) if lenmem > 0 && writeBarrier.enabled { bulkBarrierPreWriteSrcOnly(uintptr(p), uintptr(old.array), lenmem) } } //將老的數(shù)據(jù)移動到新的數(shù)據(jù) memmove(p, old.array, lenmem) return slice{p, old.len, newcap} }
總結(jié)
其實可以看出,golang的切片擴(kuò)容是比較粗暴的,直接賦值拷貝。不過,golang區(qū)分的長度和容量兩種單位計量,一般會提前分配足夠的cap,可以減少maclloc的次數(shù)。
“slice是如何擴(kuò)容的”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!
當(dāng)前文章:slice是如何擴(kuò)容的
URL鏈接:http://fisionsoft.com.cn/article/gghjjg.html