本文出自 “青客” 博客,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]
從圖中可以看出切片從數組的第4個元素開始讀取數據,直至第8個元素(但不包含第8個)。切記程序員的計數都是從0開始的喲
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])
從程序運行結果上可以看切片slice和數組intArr兩者沒有任何關係,是不同的內在地址;但有意思的是切片slice的第一個元素地址恰恰是數組intArr的第4個元素地址,這不是巧合,而是切片的實質
所以有人也說切片是指向數組的指針,對切片元素的更改會影響數組元素的值,這個通過改寫上面示例可以得到驗證:
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)
三、基於切片創建切片
能否基於切片創建切片呢?答案是肯定的,其使用方法與基於數組創建切片相同
intArr := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
slice := intArr[3:]
reslice := slice[1:]
fmt.Println(reslice)
由於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個元素的存儲空間。運行上面例子便能理解:
從結果上來看,切片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)
3、能省略容量嗎?
答案是OK的,所以可以這樣聲明切片:slice := make([]int,3)
那麼此時它的容量與元素個數相同,即爲3
4、能把元素個數和容量都省略變爲slice := make([]int)可行嗎?
答案是NO,此時GO語言就暈了,它在想難道程序員讓我把所有內存都佔用?就拋出missing len argument to make([]int)錯誤信息
5、只有類型和容量,省略元素個數呢,類似slice := make([]int,,10)
答案是NO,但可以聲明後直接初始化,即slice := []int{1,2,3,4,5,6}
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...)
程序運行追加後的結果如下:
五、切片追加元素後切片是否重新生成?
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])
從運行結果上可以看到,追加一個元素後,切片元素的地址並沒有發生變化;但在應用時必須返回值賦給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的集合很相似