Hello ,各位小伙伴大家好,我是小棧君,上次分享我們講到了Go語(yǔ)言關(guān)于項(xiàng)目工程結(jié)構(gòu)的管理,本期的分享我們來(lái)講解一下關(guān)于go語(yǔ)言的數(shù)組和切片的概念、用法和區(qū)別。
在go語(yǔ)言的程序開發(fā)過程中,我們避免不了數(shù)組和切片。關(guān)于他們的用法和區(qū)別卻使得有的小伙伴感覺困惑。所以小棧君這里也歸納和總結(jié)了關(guān)于數(shù)組和切片的干貨幫助小伙伴進(jìn)行理解。
數(shù)組的定義
數(shù)組是具有相同唯一類型的一組已編號(hào)且長(zhǎng)度固定的數(shù)據(jù)項(xiàng)序列,這種類型可以是任意的原始類型例如整形、字符串或者自定義類型。
相對(duì)于去聲明 number0, number1, ..., number99 的變量,使用數(shù)組形式 numbers[0], numbers[1] ..., numbers[99] 更加方便且易于擴(kuò)展。
數(shù)組元素可以通過索引(位置)來(lái)讀?。ɑ蛘咝薷模?,索引從 0 開始,第一個(gè)元素索引為 0,第二個(gè)索引為 1,以此類推。
總體來(lái)講的話數(shù)組就是同一種類型的固定長(zhǎng)度的序列。
在go語(yǔ)言中數(shù)組的定義是很簡(jiǎn)單的。
如圖所示,我們定義了一個(gè)長(zhǎng)度為2的數(shù)組,在數(shù)組定義的過程中,系統(tǒng)已經(jīng)對(duì)這個(gè)數(shù)組進(jìn)行了初始化并分配了空間。所以我們?nèi)绻胍M(jìn)行賦值可以通過數(shù)組名加上下標(biāo)的方式進(jìn)行賦值。但是值得注意的一點(diǎn)是我們并不能進(jìn)行數(shù)組的超長(zhǎng)處理。這一點(diǎn)有別于java的數(shù)組定義,java的定長(zhǎng)數(shù)組添加值后如果對(duì)于超出的值會(huì)有自動(dòng)擴(kuò)容功能。
但是在go語(yǔ)言中并沒有方法來(lái)進(jìn)行增刪改查值,只有通過下標(biāo)的方式,所以我們?nèi)绻M(jìn)行了越界處理編譯都會(huì)進(jìn)行報(bào)錯(cuò)。所以才入門的小伙伴們需要注意一下哦。數(shù)組的下標(biāo)在數(shù)組的合法范圍之外就會(huì)出發(fā)訪問越界,會(huì)有panic出現(xiàn)。所以小棧君也是通過了一個(gè)實(shí)例給大家說明一下,因?yàn)榫幾g可能會(huì)不通過,所以我們巧妙的避開編譯器的編譯進(jìn)行數(shù)組的越界操作說明。
當(dāng)然需要值得注意的一點(diǎn)是,數(shù)組的長(zhǎng)度也是數(shù)組類型的一部分,因此var a [2]int 和 var b [3] int 是兩個(gè)不同的類型。
知識(shí)點(diǎn)來(lái)了,在go語(yǔ)言中的數(shù)組是屬于值類型傳遞,當(dāng)我們傳遞一個(gè)數(shù)組到一個(gè)方法中,改變副本的值并不會(huì)修改到原本數(shù)組的值。所以得到的數(shù)組還是原來(lái)的樣子。
因此如果我們要對(duì)數(shù)組進(jìn)行值的修改的話,就只有進(jìn)行指針操作啦~。
切片的概念
在go語(yǔ)言中數(shù)組中長(zhǎng)度不可以更改,所以在實(shí)際的應(yīng)用環(huán)境中并不是非常實(shí)用,所以Go語(yǔ)言衍生出了一種靈活性強(qiáng)和功能更強(qiáng)大的內(nèi)置類型,即為切片。
與上面所講的數(shù)組相比,切片的長(zhǎng)度是不固定的,并且切片是可以進(jìn)行擴(kuò)容。切片對(duì)象非常小,是因?yàn)樗侵挥?個(gè)字段的數(shù)據(jù)結(jié)構(gòu):一個(gè)是指向底層數(shù)組的指針,一個(gè)是切片的長(zhǎng)度,一個(gè)是切片的容量。這3個(gè)字段,就是Go語(yǔ)言操作底層數(shù)組的元數(shù)據(jù),有了它們,我們就可以任意的操作切片了。
當(dāng)然,切片作為數(shù)組的引用,所以切片屬于是引用類型,各位小伙伴可千萬(wàn)要記住了哦。在切片的使用過程當(dāng)中,我們可以通過遍歷數(shù)組的方式進(jìn)行對(duì)于切片的遍歷,我們也可以內(nèi)置方法len對(duì)數(shù)組或切片進(jìn)行長(zhǎng)度的計(jì)算。
當(dāng)然我們也可以對(duì)切片的容量進(jìn)行計(jì)算,之前有講過Go語(yǔ)言有豐富的內(nèi)置庫(kù)提供給我們使用,所以我們也可以cap內(nèi)置函數(shù)進(jìn)行容量的計(jì)算。多個(gè)切片如果表示同一個(gè)數(shù)組的片段,它們可以共享數(shù)據(jù);因此一個(gè)切片和相關(guān)數(shù)組的其他切片是共享存儲(chǔ)的,相反,不同的數(shù)組總是代表不同的存儲(chǔ)。數(shù)組實(shí)際上是切片的構(gòu)建塊。
上面的例子小棧君分別用數(shù)組和切片進(jìn)行了測(cè)試,我們可以看到數(shù)組的容量一旦確定后就無(wú)法進(jìn)行更改,當(dāng)我們的切片進(jìn)行初始化,初始的容量是2,此時(shí)切片的容量和長(zhǎng)度都是2,但是我通過內(nèi)置的append方法進(jìn)行了切片的增加。此時(shí)的切片的容量和長(zhǎng)度都是4。此時(shí)我們并不能確定切片內(nèi)置擴(kuò)容的機(jī)制,但是隱約猜測(cè)是倍增。
言歸正傳,為了測(cè)試一下切片的擴(kuò)容機(jī)制,所以小棧君又進(jìn)行了切片的增加,此時(shí),細(xì)心的小伙伴應(yīng)該發(fā)現(xiàn),這次小棧君一次性增加了兩個(gè)元素在一個(gè)append里面,因?yàn)檫@是append方法是一個(gè)可變長(zhǎng)度的傳值。這也是一個(gè)小知識(shí)點(diǎn)哦。
如果切片的底層數(shù)組,沒有足夠的容量時(shí),就會(huì)新建一個(gè)底層數(shù)組,把原來(lái)數(shù)組的值復(fù)制到新底層數(shù)組里,再追加新值,這時(shí)候就不會(huì)影響原來(lái)的底層數(shù)組了。
append目前的算法是:容量小于1000個(gè)時(shí),總是成倍的增長(zhǎng),一旦容量超過1000個(gè),增長(zhǎng)因子設(shè)為1.25,也就是說每次會(huì)增加25%的容量。
之后我們發(fā)現(xiàn)切片的容量和長(zhǎng)度發(fā)生了變化,如果說上次容量的擴(kuò)張是4是我們猜測(cè)的倍數(shù)擴(kuò)容方式,那么這次我們就實(shí)錘了他的擴(kuò)容機(jī)制就是倍增。而且在Go語(yǔ)言的容量和長(zhǎng)度不一樣,所以我們也可以得出結(jié)論,就是在 0 <= len(arry) <= cap(slice)。
在我們聲明好切片后我們可以使用new或是make方法對(duì)切片進(jìn)行初始化,當(dāng)然小棧君也試著嘗試證明切片如果沒有進(jìn)行初始化是會(huì)panic的。結(jié)果并沒有出現(xiàn)。因?yàn)槿绻鹲lice沒有初始化,它僅僅相當(dāng)于一個(gè)nil,長(zhǎng)度和容量都為0,并不會(huì)panic。
小棧君也考慮到可能是因?yàn)闆]有內(nèi)置增加方法或是沒有報(bào)錯(cuò)僅僅只是因?yàn)槲液竺胬脤?duì)Carry數(shù)組的切割進(jìn)行賦值的緣故。所以不甘心又做了一次嘗試,定義好相應(yīng)的切片后直接使用append方法,結(jié)果如下:
我們同樣可以通過上述的例子了解到切片的下標(biāo)是左閉右開區(qū)間,因?yàn)槲覀僣arry數(shù)組的內(nèi)容如上圖所示, 我們最終得到的結(jié)果是IT干貨棧,下標(biāo)來(lái)講的話是屬于1。所以我們得到的結(jié)論和事實(shí)是一樣的。對(duì)于切片我們也有很多用法,如下圖所示:
下圖是Python中對(duì)于切片的操作,并且Python中的數(shù)組更為靈活,在后面我為大家分享Python教程的時(shí)候會(huì)詳細(xì)分享哦。
切片的初始化
對(duì)于切片的初始化我們可以make方法和new方法
new(T) 為每個(gè)新的類型T分配一片內(nèi)存,初始化為 0 并且返回類型為*T的內(nèi)存地址:這種方法 返回一個(gè)指向類型為 T,值為 0 的地址的指針,它適用于值類型如數(shù)組和結(jié)構(gòu)體;它相當(dāng)于 &T{}。
make(T) 返回一個(gè)類型為 T 的初始值,它只適用于3種內(nèi)建的引用類型:切片、map 和 channel。
拷貝
因?yàn)樵趃o語(yǔ)言的數(shù)組是屬于值傳遞,之前的方法也證實(shí)了這一點(diǎn),在方法傳遞值的時(shí)候系統(tǒng)會(huì)進(jìn)行拷貝一份副本進(jìn)行傳遞,如果需要改變的值的話就需要使用指針。但是在使用切片處理的時(shí)候,因?yàn)榍衅瑢儆谝脗鬟f,所以go語(yǔ)言有內(nèi)置的函數(shù)copy方法進(jìn)行值的拷貝。
上述的一個(gè)例子可以綜合說明幾點(diǎn)問題了,最開始我們定義了一個(gè)切片并且容量是2,內(nèi)容是1和2,我們同樣定義了切片b但是并沒有做初始化處理。直接使用copy操作。使用copy操作的時(shí)候,小棧君也復(fù)制了源碼出來(lái),第一個(gè)是我們的數(shù)據(jù)源,第二個(gè)參數(shù)傳遞我們的目標(biāo)源。直接使用的話我們可以從結(jié)果看出并沒有成功。
所以接下來(lái)小棧君又進(jìn)行了初始化操作。這里舉例的目的是提醒各位,在操作切片的時(shí)候沒有初始化就相當(dāng)于nil,最好是進(jìn)行切片的初始化操作。在早期go語(yǔ)言的版本中,沒有初始化切片會(huì)直接報(bào)錯(cuò)。接下來(lái)我又進(jìn)行了再一次的復(fù)制操作并且打印出他們的地址和容量、長(zhǎng)度。可以看出進(jìn)行切片的拷貝是不會(huì)進(jìn)行切片的擴(kuò)容處理。而且他們分別指向了不同的地址說明拷貝成功。
好了,今天的分享就到這啦,如果你喜歡我的分享,麻煩你點(diǎn)擊一個(gè)好看或贊,我是小棧君,不定期分享IT干貨,包括但不限于區(qū)塊鏈、大數(shù)據(jù)、Python、go、等系列專題。希望與你共同成長(zhǎng)。我們下期再見啦,拜了個(gè)拜~
本文由博客一文多發(fā)平臺(tái) OpenWrite 發(fā)布!