go語言教程之淺談數組和切片的異同

Hello ,各位小夥伴大家好,我是小棧君,上次分享我們講到了Go語言關於項目工程結構的管理,本期的分享我們來講解一下關於go語言的數組和切片的概念、用法和區別。

在go語言的程序開發過程中,我們避免不了數組和切片。關於他們的用法和區別卻使得有的小夥伴感覺困惑。所以小棧君這裏也歸納和總結了關於數組和切片的乾貨幫助小夥伴進行理解。

數組的定義

數組是具有相同唯一類型的一組已編號且長度固定的數據項序列,這種類型可以是任意的原始類型例如整形、字符串或者自定義類型。

相對於去聲明 number0, number1, ..., number99 的變量,使用數組形式 numbers[0], numbers[1] ..., numbers[99] 更加方便且易於擴展。

數組元素可以通過索引(位置)來讀取(或者修改),索引從 0 開始,第一個元素索引爲 0,第二個索引爲 1,以此類推。

圖片描述

總體來講的話數組就是同一種類型的固定長度的序列。

在go語言中數組的定義是很簡單的。

圖片描述

如圖所示,我們定義了一個長度爲2的數組,在數組定義的過程中,系統已經對這個數組進行了初始化並分配了空間。所以我們如果想要進行賦值可以通過數組名加上下標的方式進行賦值。但是值得注意的一點是我們並不能進行數組的超長處理。這一點有別於java的數組定義,java的定長數組添加值後如果對於超出的值會有自動擴容功能。

圖片描述

但是在go語言中並沒有方法來進行增刪改查值,只有通過下標的方式,所以我們如果進行了越界處理編譯都會進行報錯。所以才入門的小夥伴們需要注意一下哦。數組的下標在數組的合法範圍之外就會出發訪問越界,會有panic出現。所以小棧君也是通過了一個實例給大家說明一下,因爲編譯可能會不通過,所以我們巧妙的避開編譯器的編譯進行數組的越界操作說明。

圖片描述

當然需要值得注意的一點是,數組的長度也是數組類型的一部分,因此var a [2]int 和 var b [3] int 是兩個不同的類型。

知識點來了,在go語言中的數組是屬於值類型傳遞,當我們傳遞一個數組到一個方法中,改變副本的值並不會修改到原本數組的值。所以得到的數組還是原來的樣子。

圖片描述

因此如果我們要對數組進行值的修改的話,就只有進行指針操作啦~。

圖片描述

切片的概念

在go語言中數組中長度不可以更改,所以在實際的應用環境中並不是非常實用,所以Go語言衍生出了一種靈活性強和功能更強大的內置類型,即爲切片。

與上面所講的數組相比,切片的長度是不固定的,並且切片是可以進行擴容。切片對象非常小,是因爲它是隻有3個字段的數據結構:一個是指向底層數組的指針,一個是切片的長度,一個是切片的容量。這3個字段,就是Go語言操作底層數組的元數據,有了它們,我們就可以任意的操作切片了。

當然,切片作爲數組的引用,所以切片屬於是引用類型,各位小夥伴可千萬要記住了哦。在切片的使用過程當中,我們可以通過遍歷數組的方式進行對於切片的遍歷,我們也可以內置方法len對數組或切片進行長度的計算。

當然我們也可以對切片的容量進行計算,之前有講過Go語言有豐富的內置庫提供給我們使用,所以我們也可以cap內置函數進行容量的計算。多個切片如果表示同一個數組的片段,它們可以共享數據;因此一個切片和相關數組的其他切片是共享存儲的,相反,不同的數組總是代表不同的存儲。數組實際上是切片的構建塊。

圖片描述

圖片描述

上面的例子小棧君分別用數組和切片進行了測試,我們可以看到數組的容量一旦確定後就無法進行更改,當我們的切片進行初始化,初始的容量是2,此時切片的容量和長度都是2,但是我通過內置的append方法進行了切片的增加。此時的切片的容量和長度都是4。此時我們並不能確定切片內置擴容的機制,但是隱約猜測是倍增。

言歸正傳,爲了測試一下切片的擴容機制,所以小棧君又進行了切片的增加,此時,細心的小夥伴應該發現,這次小棧君一次性增加了兩個元素在一個append裏面,因爲這是append方法是一個可變長度的傳值。這也是一個小知識點哦。

如果切片的底層數組,沒有足夠的容量時,就會新建一個底層數組,把原來數組的值複製到新底層數組裏,再追加新值,這時候就不會影響原來的底層數組了。

append目前的算法是:容量小於1000個時,總是成倍的增長,一旦容量超過1000個,增長因子設爲1.25,也就是說每次會增加25%的容量。

之後我們發現切片的容量和長度發生了變化,如果說上次容量的擴張是4是我們猜測的倍數擴容方式,那麼這次我們就實錘了他的擴容機制就是倍增。而且在Go語言的容量和長度不一樣,所以我們也可以得出結論,就是在 0 <= len(arry) <= cap(slice)。

在我們聲明好切片後我們可以使用new或是make方法對切片進行初始化,當然小棧君也試着嘗試證明切片如果沒有進行初始化是會panic的。結果並沒有出現。因爲如果slice沒有初始化,它僅僅相當於一個nil,長度和容量都爲0,並不會panic。

圖片描述

小棧君也考慮到可能是因爲沒有內置增加方法或是沒有報錯僅僅只是因爲我後面利用對Carry數組的切割進行賦值的緣故。所以不甘心又做了一次嘗試,定義好相應的切片後直接使用append方法,結果如下:

圖片描述

我們同樣可以通過上述的例子瞭解到切片的下標是左閉右開區間,因爲我們carry數組的內容如上圖所示, 我們最終得到的結果是IT乾貨棧,下標來講的話是屬於1。所以我們得到的結論和事實是一樣的。對於切片我們也有很多用法,如下圖所示:

圖片描述

下圖是Python中對於切片的操作,並且Python中的數組更爲靈活,在後面我爲大家分享Python教程的時候會詳細分享哦。

圖片描述

切片的初始化

對於切片的初始化我們可以make方法和new方法

圖片描述

new(T) 爲每個新的類型T分配一片內存,初始化爲 0 並且返回類型爲*T的內存地址:這種方法 返回一個指向類型爲 T,值爲 0 的地址的指針,它適用於值類型如數組和結構體;它相當於 &T{}。

make(T) 返回一個類型爲 T 的初始值,它只適用於3種內建的引用類型:切片、map 和 channel。

拷貝

因爲在go語言的數組是屬於值傳遞,之前的方法也證實了這一點,在方法傳遞值的時候系統會進行拷貝一份副本進行傳遞,如果需要改變的值的話就需要使用指針。但是在使用切片處理的時候,因爲切片屬於引用傳遞,所以go語言有內置的函數copy方法進行值的拷貝。

圖片描述

上述的一個例子可以綜合說明幾點問題了,最開始我們定義了一個切片並且容量是2,內容是1和2,我們同樣定義了切片b但是並沒有做初始化處理。直接使用copy操作。使用copy操作的時候,小棧君也複製了源碼出來,第一個是我們的數據源,第二個參數傳遞我們的目標源。直接使用的話我們可以從結果看出並沒有成功。

所以接下來小棧君又進行了初始化操作。這裏舉例的目的是提醒各位,在操作切片的時候沒有初始化就相當於nil,最好是進行切片的初始化操作。在早期go語言的版本中,沒有初始化切片會直接報錯。接下來我又進行了再一次的複製操作並且打印出他們的地址和容量、長度。可以看出進行切片的拷貝是不會進行切片的擴容處理。而且他們分別指向了不同的地址說明拷貝成功。

好了,今天的分享就到這啦,如果你喜歡我的分享,麻煩你點擊一個好看或贊,我是小棧君,不定期分享IT乾貨,包括但不限於區塊鏈、大數據、Python、go、等系列專題。希望與你共同成長。我們下期再見啦,拜了個拜~

本文由博客一文多發平臺 OpenWrite 發佈!

發佈了33 篇原創文章 · 獲贊 7 · 訪問量 7537
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章