Golang slice 的底層實現

2021-10-25 07:51:40 字數 3421 閱讀 7378

首先我們來看段**的輸出

s := make(int, 4)

for i := 0; i < 4; i++

s0 := s[0]

fmt.println(s)

輸出的結果是

[[1 0 0 0] [1 0 0 0] [1 0 0 0] [1 0 0 0]]
帶著這些疑問,我們來看看slice的底層實現。

slice的結構

type slice struct
array指向乙個陣列元素的位址,這個陣列可能在makeslice時建立,也可能之前就存在,而slice被"attach"上去,例如 s := a[0:5];

//**比較簡單

func makeslice(et *_type, len, cap int) slice

if cap < len || uintptr(cap) > maxelements

//分配陣列空間

p := mallocgc(et.size*uintptr(cap), et, true)

return slice

}

e.g.

s := make(int, 1, 3)

fmt.printf("%p, %v, len:%d, cap:%d", unsafe.pointer(&s[0]), s, len(s), cap(s))

輸出:

0xc42007c0a0, [0], len:1, cap:3
對於直接"attach"到陣列的情形,類似下面這樣

a := [10]int

fmt.printf("%p, %v \n", &a, a)

s1 := a[1:5:7]

fmt.printf("%p, %v, len:%d, cap:%d, self:%p \n", unsafe.pointer(&s1[0]), s1, len(s1), cap(s1), unsafe.pointer(&s1) )

輸出:

0xc42006e050, [0 1 2 3 4 5 6 7 8 9] 

0xc42006e058, [1 2 3 4], len:4, cap:6, self:0xc42006c140

根據兩個指標的關係,可以看出,slice直接指向了陣列中的元素

同理,slice2還可以通過另乙個slice1構造,但其屬性依賴slice1,並不是slice1底層的陣列

s2 := s1[2:]

fmt.printf("%p, %v, len:%d, cap:%d, self:%p \n", unsafe.pointer(&s2[0]), s2, len(s2), cap(s2), unsafe.pointer(&s2) )

輸出

修改slice中元素的值,實際上修改的是底層陣列元素的值

s2[0] = 100

fmt.println(a, s1, s2)

輸出

[0 1 2 100 4 5 6 7 8 9] [1 2 100 4] [100 4]
擴張

先上**

func growslice(et *_type, old slice, cap int) slice 

return slice

}// 計算新的cap,針對不同情況分別處理

newcap := old.cap

doublecap := newcap + newcap

if cap > doublecap else else }}

var lenmem, newlenmem, capmem uintptr

const ptrsize = unsafe.sizeof((*byte)(nil))

switch et.size

......

var p unsafe.pointer

if et.kind&kindnopointers != 0 else else }}

return slice

}

fmt.printf("%p, %v \n", unsafe.pointer(&s2[0]), s2)

fmt.println(a, s1, s2)

fmt.printf("len(s1)=%d, cap(s1)=%d, len(s2)=%d, cap(s2)=%d", len(s1), cap(s1), len(s2), cap(s2)) 輸出

[0 1 2 100 4 1000 6 7 8 9] [1 2 100 4] [100 4 1000]

len(s1)=4, cap(s1)=6, len(s2)=3, cap(s2)=4

fmt.printf("%p, %v \n", unsafe.pointer(&s2[0]), s2)

fmt.println(a, s1, s2)

fmt.printf("len(s1)=%d, cap(s1)=%d, len(s2)=%d, cap(s2)=%d", len(s1), cap(s1), len(s2), cap(s2)) 輸出

0xc420014280, [100 4 1000 1001 1002] 

[0 1 2 100 4 5 6 7 8 9] [1 2 100 4] [100 4 1000 1001 1002]

len(s1)=4, cap(s1)=6, len(s2)=5, cap(s2)=8

原陣列a,切片s1的屬性未受影響;但s2底層的陣列已發生變化,cap也是之前的2倍。

1、多個slice指向相同的底層陣列時,修改其中乙個slice,可能會影響其他slice的值;

2、slice作為引數傳遞時,比陣列更為高效,因為slice的結構比較小;

3、slice在擴張時,可能會發生底層陣列的變更及記憶體拷貝;

4、因為slice引用了陣列,這可能導致陣列空間不會被gc,當陣列空間很大,而slice引用內容很少時尤為嚴重;

ConcurrentHashMap底層實現

concurrenthashmap融合了hashtable和hashmap二者的優勢 hashtable是做了同步的,hashmap沒有同步,所以hashmap在單執行緒情況下效率高,hashtable在多執行緒情況下,同步操作能保證程式執行的正確性 但是hashtable每次同步執行都要鎖住整個結...

golang slice 與list 的效能分析。

一 比較slice 與 list 遍歷建立和新增元素速度。package main import time fmt container list func main fmt.println slice 建立速度 time.now sub t string t time.now l list.new ...

Golang slice的用法以及和陣列的區別

說明 slice是go的乙個語言特性,其實有點類似於cpp的vector,可變長度,可以擴充套件空間。今天詳細看了下,做下總結。slice本質上是乙個區間,原型是t,大致的實現是這樣的 type slice struct可以看到的是是乙個指向陣列的指標,那麼在修改slice的時候會改變陣列的值。用法...