GO語言的切片

本文出自 “青客” 博客,http://qingkechina.blog.51cto.com/5552198/1616987


如果說GO語言的數組爲靜態長度的數組,那麼切片(slice)則爲動態長度的數組

一、基於數組創建切片

1、存在一個整型數組intArr := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},那麼下面的slice就是數組切片

var slice []int = intArr[3:7]


wKiom1TzEKvQ5lSYAABDmRP4YK8764.jpg

從圖中可以看出切片從數組的第4個元素開始讀取數據,直至第8個元素(但不包含第8個)。切記程序員的計數都是從0開始的喲i_f30.gif


2、若只讀intArr數組的前4個元素,該如何辦呢?聰明的你一定能想到

var slice [] int = intArr[0:4]

或者

slice := intArr[0:4]


3、是否還有更簡潔的表示嗎?那肯定了,GO這麼懂程序員

slice := intArr[:4],它與slice := intArr[0:4]等價


4、哪些讀取intArr數組第4個元素(包含第4個元素)之後的所有數據呢?

slice := intArr[3:10]

當然也可以如下表示

slice := intArr[3:len(intArr)]

或者

slice := intArr[3:]


二、基於數組創建的切片與該數組的關係

上例中基於數組intArr創建了切片slice,那麼slice與intArr有什麼關係呢?可以簡單地理解爲切片是在數組的基礎上增加了一些管理功能,很類型C++中的數組與std::vector的關係。

intArr := [10]int{1,2,3,4,5,6,7,8,9,10} // 定義數組intArr

var slice []int = intArr[3:7]        // 基於intArr創建切片 


fmt.Printf("intArr的地址是: %p\n", &intArr) 

fmt.Printf("slice的地址是 : %p\n", &slice)


fmt.Printf("intArr的第4個元素地址是: %p\n", &intArr[3])

fmt.Printf("slice的第1個元素地址是 : %p\n", &slice[0])wKiom1T0Vy7QGnb7AANZonkIBnU543.jpg

從程序運行結果上可以看切片slice和數組intArr兩者沒有任何關係,是不同的內在地址;但有意思的是切片slice的第一個元素地址恰恰是數組intArr的第4個元素地址,這不是巧合,而是切片的實質

wKiom1T0agKDLE86AADJOhQDpwA427.jpg

所以有人也說切片是指向數組的指針,對切片元素的更改會影響數組元素的值,這個通過改寫上面示例可以得到驗證:

intArr := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

slice := intArr[3:]

slice[0] = 100 // 更改切片的第1個元素值爲100

fmt.Println("slice=", slice)

fmt.Println("intArr=", intArr)

wKiom1T0awuBcatWAABUvGIMtF0935.jpg


三、基於切片創建切片

能否基於切片創建切片呢?答案是肯定的,其使用方法與基於數組創建切片相同

intArr := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

slice := intArr[3:]

reslice := slice[1:]

fmt.Println(reslice)

wKioL1T1oObxjV1yAAAmMpP3f9I678.jpg

由於slice是基於數組intArr創建的切片,它從intArr的第4個值開始,所以slice的值爲[4,5,6,7,8,9,10]; 而reslice是基於切片slice創建的切片,它從slice的第2個值開始,所以reslice的值爲[5,6,7,8,9,10]


四、直接創建切片

並不是非得基於數組或者切片才能創建切片,GO語言很貼心地爲程序員提供了make()函數,其形式爲slice := make([] type, number, capacity),例如:

slice := make([]int, 3, 10)

1、number和capacity分別是什麼意思?

number是切片元素個數,創建切片slice之後,裏面有幾個元素;capacity表示切片的容量,創建切片slice之後,先預留10個元素的存儲空間。運行上面例子便能理解:

wKioL1T1pQPSLsJfAABYae7tmek339.jpg

從結果上來看,切片slice元素個數爲3,且元素缺省填充爲0;由於切片slice的容量爲10,所以還可以向切片追加7個元素。


2、len()和cap()

切片有len()和cap()函數,分別用來獲取切片的元素個數和容量值,例如:

slice := make([]int, 3, 10)

num := len(slice)

capacity := cap(slice)

fmt.Println("切片slice元素個數爲:", num, ",容量爲:", capacity)

wKiom1T1pZyhU9tVAABCLSlHOTc784.jpg


3、能省略容量嗎?

答案是OK的,所以可以這樣聲明切片:slice := make([]int,3)

那麼此時它的容量與元素個數相同,即爲3


4、能把元素個數和容量都省略變爲slice := make([]int)可行嗎?

答案是NO,此時GO語言就暈了,它在想難道程序員讓我把所有內存都佔用?就拋出missing len argument to make([]int)錯誤信息


5、只有類型和容量,省略元素個數呢,類似slice := make([]int,,10)

答案是NOj_0081.gif,但可以聲明後直接初始化,即slice := []int{1,2,3,4,5,6}y_0003.gif


6、如何追加元素?

slice := make([]int, 3, 10)

fmt.Println("切片slice元素個數爲:", len(slice), ",容量爲:", cap(slice), ",slice=", slice)

可以這樣追加:

slice = append(slice, 4)

slice = append(slice, 5)

也可以這樣追加:

slice = append(slice, 6, 7, 8)

甚至可以追加一個切片,但在後面必須添加...

arr := [2]int{9, 10}

slice = append(slice, arr...)

程序運行追加後的結果如下:

wKiom1T1qsjCox0EAAMP71W_5V0528.jpg


五、切片追加元素後切片是否重新生成?

1、追加說明

slice := make([]int, 3, 10) // 聲明一個切片

fmt.Printf("原切片的地址%p\n", &slice)

fmt.Printf("原切片第一個元素的地址%p\n", &slice[0])


slice = append(slice, 4)  // 追加一個元素

fmt.Printf("新切片的地址%p\n", &slice)

fmt.Printf("新切片第一個元素的地址%p\n", &slice[0])

wKiom1T1r7PDJAjOAACdEk52urY469.jpg

從運行結果上可以看到,追加一個元素後,切片元素的地址並沒有發生變化;但在應用時必須返回值賦給slice,即下面的程序是錯誤的:

slice := make([]int, 3, 10) 

append(slice, 4) // 必須返回slice

fmt.Printf("%p\n", &slice)


2、追加超過容量大小

slice := make([]int, 3, 10)                   // 切片容量爲10

fmt.Printf("%p\n", &slice)                    // 打印切片地址

fmt.Println("len:", len(slice), "cap:", cap(slice))   // 打印切片元素個數和容量


slice = append(slice, 4, 5, 6, 7, 8, 9, 10, 11)      // 切片追加元素

fmt.Printf("%p\n", &slice)                    // 打印切片地址

fmt.Println("len:", len(slice), "cap:", cap(slice))   // 打印切片元素個數和容量

這個例子中一開始聲明瞭容量爲10的切片,當向切片中追加元素時,使其元素個數超過容量,此時切片會自動擴展容量一倍變爲20,這點與Java的集合很相似
wKiom1T1rgrCXhDwAALhkil8MXE502.jpg



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章