之前遇到一道感覺很不錯的slice
面試題力试,這里分享出來贮预,先不貼答案了大家可以先思考下每個地方的打印會是什么贝室;最后再給大家公布答案
v := make([]int, 0, 5)
v = append(v, 2, 3, 5)
a := append(v, 0, -1)
fmt.Println(v)
fmt.Println(a)
b := append(v, 1)
fmt.Println()
fmt.Println(v)
fmt.Println(a)
fmt.Println(b)
c := append(v, 6, 7, 8, 9)
fmt.Println()
fmt.Println(v)
fmt.Println(a)
fmt.Println(b)
fmt.Println(c)
d := append(v, 12)
fmt.Println(d)
fmt.Println(v)
fmt.Println(a)
fmt.Println(b)
fmt.Println(c)
fmt.Println(d)
在解這道面試題之前先來看下slice常用用法
一般通過make([]T,len,cap)來創(chuàng)建slice
其中cap可以省略則跟len的值相同
len 表示存儲的元素個數(shù),cap表示容量
slice 初始化姿勢
// 1仿吞、初始化時添加好了數(shù)據(jù)滑频,這時候len=cap=初始化的數(shù)據(jù)個數(shù)
s1 := []int{1, 2, 3, 4, 5, 6}
fmt.Println(s1, len(s1), cap(s1)) // [1 2 3 4 5 6] 6 6
// 2、只申明 len唤冈,cap跟len相同
s2 := make([]int, 5)
fmt.Println(s2, len(s2), cap(s2)) // [0 0 0 0 0] 5 5
// 3峡迷、同時申明len,cap
s3 := make([]int, 5, 5)
fmt.Println(s3, len(s3), cap(s3)) // [0 0 0 0 0] 5 5
// 4、先聲明一個數(shù)組你虹,從數(shù)組處申明slice
arr := [5]int{1, 2, 3, 4, 5}
s4 := arr[:]
fmt.Println(arr) // [1 2 3 4 5]
fmt.Println(s4, len(s4), cap(s4)) // [1 2 3 4 5] 5 5
slice 的len很容易理解绘搞,就是slice元素個數(shù);那cap有什么用呢傅物?下面通過例子來看下
s1 := make([]int, 5, 6)
fmt.Println(s1, len(s1), cap(s1))
fmt.Printf("%p\n", s1)
s1 = append(s1, 1)
fmt.Println(s1, len(s1), cap(s1))
fmt.Printf("%p\n", s1)
s1 = append(s1, 2)
fmt.Println(s1, len(s1), cap(s1))
fmt.Printf("%p\n", s1)
s1 = append(s1, 3)
fmt.Println(s1, len(s1), cap(s1))
fmt.Printf("%p\n", s1)
=====================================================
[0 0 0 0 0] 5 6
0xc000216000
[0 0 0 0 0 1] 6 6
0xc000216000
[0 0 0 0 0 1 2] 7 12
0xc000030660
[0 0 0 0 0 1 2 3] 8 12
0xc000030660
從上面的結(jié)果可以看出來夯辖,slice cap是包含len的,也就是說len是cap的一部分董饰,而cap-len的部分是待append數(shù)據(jù)時存放數(shù)據(jù)的部分楼雹,在打印slice時也是不展示的模孩;slice在append數(shù)據(jù)時如果cap-len還有空間則會將數(shù)據(jù)添加到這部分空間中,如果cap-len已經(jīng)為0贮缅,則slice需要擴(kuò)充整個slice的cap榨咐,擴(kuò)充的元素個數(shù)就是當(dāng)前slice的cap大小。
就如上面的例子中在append 1時 cap還有剩余空間可以放數(shù)據(jù)所以添加之后 s1的地址沒變谴供;在append 2時 cap已經(jīng)為0了所以這時候需要對s1做擴(kuò)容操作块茁;擴(kuò)容的數(shù)據(jù)量就是初始化s1時設(shè)置的cap大小6,然后將之前的數(shù)據(jù)重新copy的新的slice中桂肌,所以可以看到擴(kuò)容之后slice的地址也發(fā)生了變化数焊;擴(kuò)容之后的cap大小為12;所以在初始化slice時給個合理的cap是非常重要的崎场;因為slice在擴(kuò)容時是按照當(dāng)前cap的大小成倍增長的
slice 的本質(zhì)
slice在真正做存儲時其實是對應(yīng)著一個數(shù)組佩耳;而slice只是這個數(shù)組的視圖而已;slice的len就是slice這個視圖能夠看到這個底層數(shù)組的窗口大小谭跨,而cap是這個底層數(shù)組的大小
假如初始化一個 s := make([]int,8,13)干厚;這時候s跟底層數(shù)組如下圖所示
如上圖所示不管用那種方式初始化slice,最終在底層都是通過一個數(shù)組來存儲數(shù)據(jù)螃宙,而slice只是這個底層數(shù)組的一個視圖蛮瞄,而len就是這個視圖的窗口大小谆扎;這個類似于數(shù)據(jù)庫中的View的概念挂捅;有了這個基礎(chǔ)之后我來再回頭看文章開頭說的面試題就很好做了
我們先把文章開頭的面試題以及答案貼出來,下面再通過圖示的方式來解答
// v底層數(shù)組值 2,3,5,0,-1
v := make([]int, 0, 5)
v = append(v, 2, 3, 5)
a := append(v, 0, -1)
fmt.Println(v) // 2,3,5
fmt.Println(a) // 2,3,5,0,-1
// v底層數(shù)組值 2,3,5,1,-1
b := append(v, 1)
fmt.Println()
fmt.Println(v) // 2,3,5
fmt.Println(a) // 2,3,5,1,-1
fmt.Println(b) // 2,3,5,1
// v底層數(shù)組值 2,3,5,1,-1,6,7,8,9
c := append(v, 6, 7, 8, 9)
fmt.Println()
fmt.Println(v) // 2,3,5
fmt.Println(a) // 2,3,5,1,-1
fmt.Println(b) // 2,3,5,1
fmt.Println(c) // 2,3,5,6,7,8,9
// v底層數(shù)組值 2,3,5,12,-1
d := append(v, 12)
fmt.Println()
fmt.Println(d) // 2,3,5,12
fmt.Println(v) // 2,3,5
fmt.Println(a) // 2,3,5,12,-1
fmt.Println(b) // 2,3,5,12
fmt.Println(c) // 2,3,5,1,6,7,8,9
fmt.Println(d) // 2,3,5,12
1堂湖、第一層打印
v := make([]int, 0, 5)
v = append(v, 2, 3, 5)
a := append(v, 0, -1)
fmt.Println(v) // 2,3,5
fmt.Println(a) // 2,3,5,0,-1
2闲先、第二層打印
b := append(v, 1)
fmt.Println()
fmt.Println(v) // 2,3,5
fmt.Println(a) // 2,3,5,1,-1
fmt.Println(b) // 2,3,5,1
3、第三層打印
// v底層數(shù)組值 2,3,5,1,-1,6,7,8,9
c := append(v, 6, 7, 8, 9)
fmt.Println()
fmt.Println(v) // 2,3,5
fmt.Println(a) // 2,3,5,1,-1
fmt.Println(b) // 2,3,5,1
fmt.Println(c) // 2,3,5,6,7,8,9
4无蜂、第四層打印
d := append(v, 12)
fmt.Println()
fmt.Println(d) // 2,3,5,12
fmt.Println(v) // 2,3,5
fmt.Println(a) // 2,3,5,12,-1
fmt.Println(b) // 2,3,5,12
fmt.Println(c) // 2,3,5,1,6,7,8,9
fmt.Println(d) // 2,3,5,12