這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)碛嘘P(guān)如何在Golang中使用切片,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
成都創(chuàng)新互聯(lián)公司專注于企業(yè)全網(wǎng)營(yíng)銷推廣、網(wǎng)站重做改版、嘉祥網(wǎng)站定制設(shè)計(jì)、自適應(yīng)品牌網(wǎng)站建設(shè)、H5場(chǎng)景定制、商城網(wǎng)站制作、集團(tuán)公司官網(wǎng)建設(shè)、成都外貿(mào)網(wǎng)站建設(shè)、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁(yè)設(shè)計(jì)等建站業(yè)務(wù),價(jià)格優(yōu)惠性價(jià)比高,為嘉祥等各大城市提供網(wǎng)站開發(fā)制作服務(wù)。前言
Go 數(shù)組的長(zhǎng)度不可改變,在特定場(chǎng)景中這樣的集合就不太適用,Go中提供了一種靈活,功能強(qiáng)悍的內(nèi)置類型切片("動(dòng)態(tài)數(shù)組"),與數(shù)組相比切片的長(zhǎng)度是不固定的,可以追加元素,在追加時(shí)可能使切片的容量增大
Go的切片類型為處理同類型數(shù)據(jù)序列提供一個(gè)方便而高效的方式。 切片有些類似于其他語(yǔ)言中的數(shù)組,但是有一些不同尋常的特性。 本文將深入切片的本質(zhì),并講解它的用法。
數(shù)組
Go的切片是在數(shù)組之上的抽象數(shù)據(jù)類型,因此在了解切片之前必須要先理解數(shù)組。
數(shù)組類型定義了長(zhǎng)度和元素類型。例如, [4]int 類型表示一個(gè)四個(gè)整數(shù)的數(shù)組。 數(shù)組的長(zhǎng)度是固定的,長(zhǎng)度是數(shù)組類型的一部分( [4]int 和 [5]int 是完全不同的類型)。 數(shù)組可以以常規(guī)的索引方式訪問,表達(dá)式 s[n] 訪問數(shù)組的第 n 個(gè)元素。
var a [4]int a[0] = 1 i := a[0] // i == 1
數(shù)組不需要顯式的初始化;數(shù)組的零值是可以直接使用的,數(shù)組元素會(huì)自動(dòng)初始化為其對(duì)應(yīng)類型的零值:
// a[2] == 0, int 類型的零值
類型 [4]int 對(duì)應(yīng)內(nèi)存中四個(gè)連續(xù)的整數(shù):
Go的數(shù)組是值語(yǔ)義。一個(gè)數(shù)組變量表示整個(gè)數(shù)組,它不是指向第一個(gè)元素的指針(不像 C 語(yǔ)言的數(shù)組)。 當(dāng)一個(gè)數(shù)組變量被賦值或者被傳遞的時(shí)候,實(shí)際上會(huì)復(fù)制整個(gè)數(shù)組。 (為了避免復(fù)制數(shù)組,你可以傳遞一個(gè)指向數(shù)組的指針,但是數(shù)組指針并不是數(shù)組。) 可以將數(shù)組看作一個(gè)特殊的struct,結(jié)構(gòu)的字段名對(duì)應(yīng)數(shù)組的索引,同時(shí)成員的數(shù)目固定。
數(shù)組的字面值像這樣:
b := [2]string{"Penn", "Teller"}
當(dāng)然,也可以讓編譯器統(tǒng)計(jì)數(shù)組字面值中元素的數(shù)目:
b := [...]string{"Penn", "Teller"}
這兩種寫法, b 都是對(duì)應(yīng) [2]string 類型。
切片
數(shù)組雖然有適用它們的地方,但是數(shù)組不夠靈活,因此在Go代碼中數(shù)組使用的并不多。 但是,切片則使用得相當(dāng)廣泛。切片基于數(shù)組構(gòu)建,但是提供更強(qiáng)的功能和便利。
切片類型的寫法是 []T , T 是切片元素的類型。和數(shù)組不同的是,切片類型并沒有給定固定的長(zhǎng)度。
切片的字面值和數(shù)組字面值很像,不過切片沒有指定元素個(gè)數(shù):
letters := []string{"a", "b", "c", "d"}
切片可以使用內(nèi)置函數(shù) make 創(chuàng)建,函數(shù)簽名為:
func make([]T, len, cap) []T
其中T代表被創(chuàng)建的切片元素的類型。函數(shù) make 接受一個(gè)類型、一個(gè)長(zhǎng)度和一個(gè)可選的容量參數(shù)。 調(diào)用 make 時(shí),內(nèi)部會(huì)分配一個(gè)數(shù)組,然后返回?cái)?shù)組對(duì)應(yīng)的切片。
var s []byte s = make([]byte, 5, 5) // s == []byte{0, 0, 0, 0, 0}
當(dāng)容量參數(shù)被忽略時(shí),它默認(rèn)為指定的長(zhǎng)度。下面是簡(jiǎn)潔的寫法:
s := make([]byte, 5)
可以使用內(nèi)置函數(shù) len 和 cap 獲取切片的長(zhǎng)度和容量信息。
len(s) == 5 cap(s) == 5
接下來的兩個(gè)小節(jié)將討論長(zhǎng)度和容量之間的關(guān)系。
切片的零值為 nil 。對(duì)于切片的零值, len 和 cap 都將返回0。
切片也可以基于現(xiàn)有的切片或數(shù)組生成。切分的范圍由兩個(gè)由冒號(hào)分割的索引對(duì)應(yīng)的半開區(qū)間指定。 例如,表達(dá)式 b[1:4] 創(chuàng)建的切片引用數(shù)組 b 的第1到3個(gè)元素空間(對(duì)應(yīng)切片的索引為0到2)。
b := []byte{'g', 'o', 'l', 'a', 'n', 'g'} // b[1:4] == []byte{'o', 'l', 'a'}, sharing the same storage as b
切片的開始和結(jié)束的索引都是可選的;它們分別默認(rèn)為零和數(shù)組的長(zhǎng)度。
// b[:2] == []byte{'g', 'o'} // b[2:] == []byte{'l', 'a', 'n', 'g'} // b[:] == b
下面語(yǔ)法也是基于數(shù)組創(chuàng)建一個(gè)切片:
x := [3]string{"Лайка", "Белка", "Стрелка"} s := x[:] // a slice referencing the storage of x
切片的內(nèi)幕
一個(gè)切片是一個(gè)數(shù)組片段的描述。它包含了指向數(shù)組的指針,片段的長(zhǎng)度, 和容量(片段的大長(zhǎng)度)。
前面使用 make([]byte, 5) 創(chuàng)建的切片變量 s 的結(jié)構(gòu)如下:
長(zhǎng)度是切片引用的元素?cái)?shù)目。容量是底層數(shù)組的元素?cái)?shù)目(從切片指針開始)。 關(guān)于長(zhǎng)度和容量和區(qū)域?qū)⒃谙乱粋€(gè)例子說明。
我們繼續(xù)對(duì) s 進(jìn)行切片,觀察切片的數(shù)據(jù)結(jié)構(gòu)和它引用的底層數(shù)組:
s = s[2:4]
切片操作并不復(fù)制切片指向的元素。它創(chuàng)建一個(gè)新的切片并復(fù)用原來切片的底層數(shù)組。 這使得切片操作和數(shù)組索引一樣高效。因此,通過一個(gè)新切片修改元素會(huì)影響到原始切片的對(duì)應(yīng)元素。
d := []byte{'r', 'o', 'a', 'd'} e := d[2:] // e == []byte{'a', 'd'} e[1] = 'm' // e == []byte{'a', 'm'} // d == []byte{'r', 'o', 'a', 'm'}
前面創(chuàng)建的切片 s 長(zhǎng)度小于它的容量。我們可以增長(zhǎng)切片的長(zhǎng)度為它的容量:
s = s[:cap(s)]
切片增長(zhǎng)不能超出其容量。增長(zhǎng)超出切片容量將會(huì)導(dǎo)致運(yùn)行時(shí)異常,就像切片或數(shù)組的索引超 出范圍引起異常一樣。同樣,不能使用小于零的索引去訪問切片之前的元素。
切片的生長(zhǎng)(copy and append 函數(shù))
要增加切片的容量必須創(chuàng)建一個(gè)新的、更大容量的切片,然后將原有切片的內(nèi)容復(fù)制到新的切片。 整個(gè)技術(shù)是一些支持動(dòng)態(tài)數(shù)組語(yǔ)言的常見實(shí)現(xiàn)。下面的例子將切片 s 容量翻倍,先創(chuàng)建一個(gè)2倍 容量的新切片 t ,復(fù)制 s 的元素到 t ,然后將 t 賦值給 s :
t := make([]byte, len(s), (cap(s)+1)*2) // +1 in case cap(s) == 0 for i := range s { t[i] = s[i] } s = t
循環(huán)中復(fù)制的操作可以由 copy 內(nèi)置函數(shù)替代。copy 函數(shù)將源切片的元素復(fù)制到目的切片。 它返回復(fù)制元素的數(shù)目。
func copy(dst, src []T) int
copy 函數(shù)支持不同長(zhǎng)度的切片之間的復(fù)制(它只復(fù)制較短切片的長(zhǎng)度個(gè)元素)。 此外, copy 函數(shù)可以正確處理源和目的切片有重疊的情況。
使用 copy 函數(shù),我們可以簡(jiǎn)化上面的代碼片段:
t := make([]byte, len(s), (cap(s)+1)*2) copy(t, s) s = t
一個(gè)常見的操作是將數(shù)據(jù)追加到切片的尾部。下面的函數(shù)將元素追加到切片尾部, 必要的話會(huì)增加切片的容量,最后返回更新的切片:
func AppendByte(slice []byte, data ...byte) []byte { m := len(slice) n := m + len(data) if n > cap(slice) { // if necessary, reallocate // allocate double what's needed, for future growth. newSlice := make([]byte, (n+1)*2) copy(newSlice, slice) slice = newSlice } slice = slice[0:n] copy(slice[m:n], data) return slice }
下面是 AppendByte 的一種用法:
p := []byte{2, 3, 5} p = AppendByte(p, 7, 11, 13) // p == []byte{2, 3, 5, 7, 11, 13}
類似 AppendByte 的函數(shù)比較實(shí)用,因?yàn)樗峁┝饲衅萘吭鲩L(zhǎng)的完全控制。 根據(jù)程序的特點(diǎn),可能希望分配較小的活較大的塊,或則是超過某個(gè)大小再分配。
但大多數(shù)程序不需要完全的控制,因此Go提供了一個(gè)內(nèi)置函數(shù) append , 用于大多數(shù)場(chǎng)合;它的函數(shù)簽名:
func append(s []T, x ...T) []T
append 函數(shù)將 x 追加到切片 s 的末尾,并且在必要的時(shí)候增加容量。
a := make([]int, 1) // a == []int{0} a = append(a, 1, 2, 3) // a == []int{0, 1, 2, 3}
如果是要將一個(gè)切片追加到另一個(gè)切片尾部,需要使用 ... 語(yǔ)法將第2個(gè)參數(shù)展開為參數(shù)列表。
a := []string{"John", "Paul"} b := []string{"George", "Ringo", "Pete"} a = append(a, b...) // equivalent to "append(a, b[0], b[1], b[2])" // a == []string{"John", "Paul", "George", "Ringo", "Pete"}
由于切片的零值 nil 用起來就像一個(gè)長(zhǎng)度為零的切片,我們可以聲明一個(gè)切片變量然后在循環(huán) 中向它追加數(shù)據(jù):
// Filter returns a new slice holding only // the elements of s that satisfy f() func Filter(s []int, fn func(int) bool) []int { var p []int // == nil for _, v := range s { if fn(v) { p = append(p, v) } } return p }
可能的“陷阱”
正如前面所說,切片操作并不會(huì)復(fù)制底層的數(shù)組。整個(gè)數(shù)組將被保存在內(nèi)存中,直到它不再被引用。 有時(shí)候可能會(huì)因?yàn)橐粋€(gè)小的內(nèi)存引用導(dǎo)致保存所有的數(shù)據(jù)。
例如, FindDigits 函數(shù)加載整個(gè)文件到內(nèi)存,然后搜索第一個(gè)連續(xù)的數(shù)字,最后結(jié)果以切片方式返回。
var digitRegexp = regexp.MustCompile("[0-9]+") func FindDigits(filename string) []byte { b, _ := ioutil.ReadFile(filename) return digitRegexp.Find(b) }
這段代碼的行為和描述類似,返回的 []byte 指向保存整個(gè)文件的數(shù)組。因?yàn)榍衅昧嗽嫉臄?shù)組, 導(dǎo)致 GC 不能釋放數(shù)組的空間;只用到少數(shù)幾個(gè)字節(jié)卻導(dǎo)致整個(gè)文件的內(nèi)容都一直保存在內(nèi)存里。
要修復(fù)整個(gè)問題,可以將感興趣的數(shù)據(jù)復(fù)制到一個(gè)新的切片中:
func CopyDigits(filename string) []byte { b, _ := ioutil.ReadFile(filename) b = digitRegexp.Find(b) c := make([]byte, len(b)) copy(c, b) return c }
上述就是小編為大家分享的如何在Golang中使用切片了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)成都網(wǎng)站設(shè)計(jì)公司行業(yè)資訊頻道。
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場(chǎng)景需求。