歡迎加入GolangRoadmap,一個年輕的GO開發者社群https://www.golangroadmap.com/,目前是邀請制注冊,注冊碼:Gopher-1035-0722,已開放GO内推,GO面試,GO寶典,GO返利等欄目
切片
切片通過對數組進行封裝,為資料序列提供了更通用、強大而友善的接口。 除了矩陣變換這類需要明确次元的情況外,Go中的大部分數組程式設計都是通過切片來完成的。
切片儲存了對底層數組的引用,若你将某個切片賦予另一個切片,它們會引用同一個數組。 若某個函數将一個切片作為參數傳入,則它對該切片元素的修改對調用者而言同樣可見, 這可以了解為傳遞了底層數組的指針。是以,Read 函數可接受一個切片實參 而非一個指針和一個計數;切片的長度決定了可讀取資料的上限。以下為 os 包中 File 類型的 Read 方法簽名:
該方法傳回讀取的位元組數和一個錯誤值(若有的話)。若要從更大的緩沖區 b 中讀取前32個位元組,隻需對其進行切片即可。
這種切片的方法常用且高效。若不談效率,以下片段同樣能讀取該緩沖區的前32個位元組。
var n int
var err error
for i := 0; i < 32; i++ {
nbytes, e := f.Read(buf[i:i+1]) // Read one byte.
n += nbytes
if nbytes == 0 || e != nil {
err = e
break
}
}
隻要切片不超出底層數組的限制,它的長度就是可變的,隻需将它賦予其自身的切片即可。 切片的容量可通過内建函數 cap 獲得,它将給出該切片可取得的最大長度。 以下是将資料追加到切片的函數。若資料超出其容量,則會重新配置設定該切片。傳回值即為所得的切片。 該函數中所使用的 len 和 cap 在應用于 nil 切片時是合法的,它會傳回 0。
func Append(slice, data []byte) []byte {
l := len(slice)
if l + len(data) > cap(slice) { // 重新配置設定
// 為未來的增長,雙重配置設定所需的内容.
newSlice := make([]byte, (l+len(data))*2)
// copy函數是預先聲明的,适用于任何切片類型。
copy(newSlice, slice)
slice = newSlice
}
slice = slice[0:l+len(data)]
copy(slice[l:], data)
return slice
}
最終我們必須傳回切片,因為盡管 Append 可修改 slice 的元素,但切片自身(其運作時資料結構包含指針、長度和容量)是通過值傳遞的。
向切片追加東西的想法非常有用,是以有專門的内建函數 append。 要了解該函數的設計,我們還需要一些額外的資訊,我們将稍後再介紹它。
二維切片
Go的數組和切片都是一維的。要建立等價的二維數組或切片,就必須定義一個數組的數組, 或切片的切片,就像這樣:
type Transform [3][3]float64 // 一個 3x3 的數組,其實是包含多個數組的一個數組。
type LinesOfText [][]byte // 包含多個位元組切片的一個切片。
由于切片長度是可變的,是以其内部可能擁有多個不同長度的切片。在我們的 LinesOfText 例子中,這是種常見的情況:每行都有其自己的長度。
text := LinesOfText{
[]byte("Now is the time"),
[]byte("for all good gophers"),
[]byte("to bring some fun to the party."),
}
有時必須配置設定一個二維數組,例如在處理像素的掃描行時,這種情況就會發生。 我們有兩種方式來達到這個目的。一種就是獨立地配置設定每一個切片;而另一種就是隻配置設定一個數組, 将各個切片都指向它。采用哪種方式取決于你的應用。若切片會增長或收縮, 就應該通過獨立配置設定來避免覆寫下一行;若不會,用單次配置設定來構造對象會更加高效。 以下是這兩種方法的大概代碼,僅供參考。首先是一次一行的:
// 配置設定底層切片.
picture := make([][]uint8, YSize) // y每一行的大小
//循環周遊每一行
for i := range picture {
picture[i] = make([]uint8, XSize)
}
現在是一次配置設定,對行進行切片:
// 配置設定底層切片
picture := make([][]uint8, YSize) // 每 y 個單元一行。
// 配置設定一個大一些的切片以容納所有的元素
pixels := make([]uint8, XSize*YSize) // 指定類型[]uint8, 即便圖檔是 [][]uint8.
//循環周遊圖檔所有行,從剩餘像素切片的前面對每一行進行切片。
for i := range picture {
picture[i], pixels = pixels[:XSize], pixels[XSize:]
}