上篇咱們介紹了Go語言的數組類型,本篇將介紹Go語言的切片類型。主要以下:數組
切片能夠看做是對數組的一種包裝形式。切片包裝的數組稱爲該切片的底層數組。切片是針對其底層數組中某個連續片斷的描述符。數據結構
對於一個元素類型爲T的切片類型來講,它的類型字面量就是:app
[]T
能夠看出,長度並非切片類型的一部分(即它不會出如今表示切片類型的類型字面量中)。另外,切片的長度是可變的。相同類型的切片值可能會有不一樣的長度。ide
切片類型聲明中的元素類型也能夠是任意一個有效的Go語言數據類型。例如:函數
[]rune
如上用於表示元素類型爲rune的切片類型。3d
一樣能夠把一個匿名結構體類型做爲切片類型的元素類型。例如:指針
[] struct {name, department string}
和數組相似,也是複合字面量中的一種,例如:code
[]string{"Go", "Python", "Java", "C", "C++", "PHP"}
在切片值所屬的類型中根本就沒有關於長度的規定。如下切片是合法的:對象
[]string{8: "Go", 2: "Swift", "Java", "C", "C++", "PHP"}
上面的等同於下面的複合字面量:blog
[]string{0: "", 1: "", 2: "Swift", 3: "Java", 4: "C", 5: "C++", 6: "PHP", 7: "", 8: "Go"}
切片類型的零值爲nil。在初始化以前,一個切片類型的變量值爲nil。
切片類型中雖然沒有關於長度的聲明,可是值是有長度的,體如今它們所包含的元素值的實際數量。可使用內建函數len來獲取切片值的長度。例如:
len([]string{8: "Go", 2: "Swift", "Java", "C", "C++", "PHP"})
上面計算的結果值爲9,這個切片值實際包含了6個被明確指定的string類型值和3個被填充的string類型的零值「」。
注意:在切片類型的零值(即nil)上應用內建函數len會獲得0。
切片值的底層實現方式:
一個切片值總會持有一個對某個數組值的引用。一個切片值一旦被初始化,就會與一個包含了其中元素值的數組值相關聯。這個數組值被稱爲引用他的切片值的底層數組。
多個切片值可能會共用一個底層數組。例如,若是把一個切片值複製成多個,或者針對其中的某個連續片斷在切片成新的切片值,那麼這些切片值所引用的都會是同一個底層數組。對切片值中的元素值的修改,實質上就是對其底層數組上的對應元素的修改。反過來說,對做爲底層元素的數組值中的元素值的改變,也會體現到引用該底層數組其包含該元素值的全部切片值上。
除了長度以外,切片值還有一個很重要的屬性-----容量。切片值的容量與它所持有的底層數組的長度有關。能夠經過內建函數cap來獲取它。例如:
cap([]string{8: "Go", 2: "Swift", "Java", "C", "C++", "PHP"})
該切片值的容量是9,就等於它的長度。這是個特例,但不少狀況下不是這樣,且聽慢慢道來。
切片值的底層數據結構:
一個切片值的底層數據結構包含了一個指向底層數組的指針類型值,一個表明了切片長度的int類型值和一個表明了切片容量的int類型值。
可使用切片表達式從一個數組值或者切片值上」切」出一個連續片斷,並生成一個新的切片值。例如:
array1 := [...]string{8: "Go", 2: "Swift", "Java", "C", "C++", "PHP"} slice1 := array1[:4]
變量slice1的值的底層數組實際上就是變量array1的值,以下圖:
通過上面的描述,你們可能認爲一個切片的容量可能就是其底層數組的長度。但事實並不是如此,這裏再建立一個切片值。例如:
slice2 := array1[3:]
變量slice2的值的底層數組也是變量array1的值,以下圖:
如上所示slice2的值的容量與array1的值的長度並不相等。實際上,一個切片值的容量是從其中的指針指向的那個元素值到底層數組的最後一個元素值的計數值。切片值的容量的含義是其可以訪問到的當前底層數組中的元素值的最大數量。
能夠對切片值進行擴展,以查看更多底層數組元素。可是,並不能直接經過再切片的方式來擴展窗口。例如對於上面原始的slice1的值進行以下操做:
slice1[4]
這會引發一個運行時恐慌,由於其中的索引值超出了這個切片值當前的長度,這是不容許的。正確拓展的方式以下:
slice1 = slice1[:cap(slice1)]
經過再切片的方式把slice1擴展到了最大,能夠看到最多的底層數組元素值了。這時slice1的值的長度等於其容量。
注意:一個切片值的容量是固定的。也就是說,可以看到的底層數組元素的最大數量是固定的。
不能把切片值擴展到其容量以外,例如:
slice1 = slice1[:cap(slice1)+1]//超出slice1容量的範圍,這樣會引發一個運行時恐慌
一個切片值只能向索引遞增的方向擴展。例如:
slice2 = slice2[-2:] //這會引發一個運行時恐慌。另外,切片值不容許由負整數字面量表明。
使用append函數來擴展一開始的slice1的值:
slice1 = append(slice1, "Ruby", "Erlang")
執行該語句後,切片類型變量slice1的值及其底層數組(數組變量array1的值)的狀態,以下圖:
能夠看出,slice1的值的長度已經由原來的4增加到了6,與它的容量是相同的。可是因爲這個值的長度尚未超出它的容量,因此不必再建立出一個新的底層數組出來。
原來的slice1的值爲:
[]string{"Go", "Python", "Java", "C"}
如今的slice1的值爲:
[]string{"Go", "Python", "Java", "C", "Ruby", "Erlang"}
原來的array1的值爲:
[6]string{"Go", "Python", "Java", "C", "C++", "PHP"}
如今的array1的值爲:
[6]string{"Go", "Python", "Java", "C", "Ruby", "Erlang"}
對如今的slice1再進行擴展,以下:
slice1 = append(slice1, "Lisp")
執行這條語句後,變量slice1的值的長度就超出了它的容量。這時將會有一個新的數組值被建立並初始化。這個新的數組值將做爲在append函數新建立的切片值的底層數組,幷包含原切片值中的所有元素值以及做爲擴展內容的全部元素值。新切片值中的指針將指向其底層數組的第一個元素值,且它長度和容量都與其底層數組的長度相同。最後,這個新的切片值會被賦給變量slice1。
可使用append函數把兩個元素類型相同的切片值鏈接起來。例如:
slice1 = append(slice1, slice...)
固然也能夠把數組值做爲第二個參數傳遞給append函數。
即便切片類型的變量的值爲零值nil,也會被看做是長度爲0的切片值。例如:
slice2 = nil slice2 = append(slice2, slice1...)
或者以下:
var slice4 []string slice4 = append(slice4, slice...)
上面第一條語句用於聲明(不包含初始化)一個變量。以關鍵字var做爲開始,並後跟變量的名稱和類型。未被初始化的切片變量的值爲nil。
切片表達式中添加第三個索引-----容量上界索引。若是被指定,那麼切片表達式的求值結果的那個切片值的容量就再也不是該切片表達式的操做對象的容量與該表達式中的元素下界索引之間的差值,而是容量上界索引與元素下界索引之間的差值。
指定容量上界索引的目的就是爲了減少新切片值的容量,能夠容許更加靈活的數據隔離策略。
var array2 [10]int = [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} slice5 := array2[2:6]
如上咱們能夠直接訪問和修改array2中對應索引值在[2,6)範圍以內的元素值。
slice5 = slice5[:cap(slice5)]
如上再切片後,能夠訪問和修改array2的值中對應索引值在[2,10)範圍以內的元素值。
若是slice5的值做爲數據載體傳遞給了另外一個程序,那麼這個程序能夠隨意地更改array2的值中的某些元素值。這就等於暴露了程序中的部分實現細節,並公開了一個能夠間接修改程序內部狀態的方法,而每每這並非咱們想要的。
若是這樣聲明slice5:
slice5 := array2[2:6:8]
這樣slice5的持有者只能訪問和修改array2的值中對應索引值在[2,8)範圍以內的元素值。
slice5 = slice5[:cap(slice5)]
即便將slice5擴展到最大,也不能經過它訪問到array2的值中對應索引值大於等於8的那些元素。此時,slice5的值的容量爲6(容量上界索引與元素下界索引的差值)。對於切片操做來講,被操做對象的容量是一個不可逾越的限制。slice5的值對其底層數組(array2的值)的「訪問權限」獲得了限制。
若是在slice5的值之上的擴展超出了它的容量:
slice5 = append(slice5, []int{10, 11, 12, 13, 14, 15}…)
那麼它原有的底層數組就會被替換。也就完全切斷了經過slice5訪問和修改其原有底層數組中的元素值的途徑。
切片表達式中的3個索引的限制:當在切片表達式中指定容量上界索引的時候,元素上界索引是不可以省略。可是,在這種狀況下元素下界索引倒是能夠省略的。例如:
slice5[:3:5]//合法的切片表達式 slice5[0::5]//非法的切片表達式,會形成一個編譯錯誤
批量複製切片值中的元素
sliceA := []string{"Notepad", "UltraEdit", "Eclipse"} sliceB := []string{"Vim", "Emacs", "LiteIDE", "IDEA"}
使用Go語言的內建函數copy,將變量sliceB的值中的元素複製到sliceA的值中。例如:
n1 := copy(sliceA,sliceB)
內建函數copy的做用是把源切片值(第二個參數值)中的元素值複製到目標切片值(第一個參數值)中,而且返回被複制的元素值的數量。copy函數的兩個參數的元素類型必須一致,且它實際複製的元素值的數量將等於長度較短的那個切片值的長度。
變量n1的值爲3, 變量sliceA的值被修改成:
[]string{"Vim", "Emacs", "LiteIDE"}