這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
《Go語言實戰》讀書筆記,未完待續,歡迎掃碼關注公眾號flysnow_org
,第一時間看後續筆記。
數組,是用來儲存集合資料的,這種情境非常多,我們編碼的過程中,都少不了要讀取或者儲存資料。當然除了數組之外,我們還有切片、Map映射等資料結構可以幫我們儲存資料,但是數組是它們的基礎。
內部實現
要想更清晰的瞭解數組,我們得瞭解它的內部實現。數組是長度固定的資料類型,必須儲存一段相同類型的元素,而且這些元素是連續的。我們這裡強調固定長度,可以說這是和切片最明顯的區別。
數組儲存的類型可以是內建類型,比如整型或者字串,也可以是自訂的資料結構。因為是連續的,所以索引比較好計算,所以我們可以很快的索引數組中的任何資料。
這裡的索引,一直都是0,1,2,3這樣的,因為其元素類型相同,我們也可以使用反射,擷取類型佔用大小,進行移位,擷取相應的元素,這個反射的時候,我們再講。
聲明和初始化
數組的聲明和初始化,和其他類型差不多。聲明的原則是:
- 指明儲存資料的類型。
- 儲存元素的數量,也就是數組長度。
以上我們聲明了一個數組array
,但是我們還沒有對他進行初始化,這時候數組array
裡面的值,是對應元素類型的零值,也就是說,現在這個數組是5個0,這和我們java不一樣,java裡是null。
數組一旦聲明後,其元素類型和大小都不能變了,如果還需要儲存更多的元素怎麼辦?那麼只能通過建立一個新的數組,然後把原來數組的資料複製過去。
剛剛聲明的數組已經被預設的元素類型零值初始化了,如果我們再次進行初始化怎麼做呢,可以採用如下辦法:
12 |
var array [5]intarray = [5]int{1,2,3,4,5} |
這兩步比較繁瑣,Go為我們提供了:=
操作符,可以讓我們在建立數組的時候直接初始化。
1 |
array:=[5]int{1,2,3,4,5} |
這種簡短變數聲明的方式不僅適用於數組,還適用於任何資料類型,這也是Go語言中常用的方式。
有時候我們更懶,連數組的長度都不想指定,不過沒有關係,使用...
代替就好了,Go會自動推匯出數組的長度。
1 |
array:=[...]int{1,2,3,4,5} |
假如我們只想給索引為1和3的數組初始化相應的值,其他都為0怎麼做呢,直接的辦法有:
1 |
array:=[5]int{0,1,0,4,0} |
還有一種更好的辦法,上面講預設初始化為零值,那麼我們就可以利用這個特性,只初始化索引1和3的值:
使用數組
數組的訪問非常簡單,通過索引即可,操作符為[]
,因為記憶體是連續的,所以索引訪問的效率非常高。
12 |
array:=[5]int{1:1,3:4}fmt.Printf("%d",array[1]) |
修改數組中的一個元素也很簡單:
1234 |
array:=[5]int{1:1,3:4}fmt.Printf("%d\n",array[1])array[1] = 3fmt.Printf("%d\n",array[1]) |
如果我們要迴圈列印數組中的所有值,一個傳統的就是常用的for迴圈:
1234567 |
func main() {array := [5]int{1: 1, 3: 4}for i := 0; i < 5; i++ {fmt.Printf("索引:%d,值:%d\n", i, array[i])}} |
不過大部分時候,我們都是使用for rang迴圈:
1234567 |
func main() {array := [5]int{1: 1, 3: 4}for i, v := range array {fmt.Printf("索引:%d,值:%d\n", i, v)}} |
這兩段範例程式碼,輸出的結果是一樣的。
同樣類型的數組是可以相互賦值的,不同類型的不行,會編譯錯誤。那麼什麼是同樣類型的數組呢?Go語言規定,必須是長度一樣,並且每個元素的類型也一樣的數組,才是同樣類型的數組。
123 |
array := [5]int{1: 1, 3: 4}var array1 [5]int = array //successvar array2 [4]int = array1 //error |
指標數組和數組本身差不多,只不過元素類型是指標。
1 |
array := [5]*int{1: new(int), 3:new(int)} |
這樣就建立了一個指標數組,並且為索引1和3都建立了記憶體空間,其他索引是指標的零值nil
,這時候我們要修改指標變數的值也很簡單,如下即可:
12 |
array := [5]*int{1: new(int), 3:new(int)}*array[1] = 1 |
以上需要注意的是,只可以給索引1和3賦值,因為只有它們分配了記憶體,才可以賦值,如果我們給索引0賦值,啟動並執行時候,會提示無效記憶體或者是一個nil指標引用。
1 |
panic: runtime error: invalid memory address or nil pointer dereference |
要解決這個問題,我們要先給索引0分配記憶體,然後再進行賦值修改。
1234 |
array := [5]*int{1: new(int), 3:new(int)}array[0] =new(int)*array[0] = 2fmt.Println(*array[0]) |
函數間傳遞數組
在函數間傳遞變數時,總是以值的方式,如果變數是個數組,那麼就會整個複製,並傳遞給函數,如果數組非常大,比如長度100多萬,那麼這對記憶體是一個很大的開銷。
12345678910 |
func main() {array := [5]int{1: 2, 3:4}modify(array)fmt.Println(array)}func modify(a [5]int){a[1] =3fmt.Println(a)} |
通過上面的例子,可以看到,數組是複製的,原來的數組沒有修改。我們這裡是5個長度的數組還好,如果有幾百萬怎麼辦,有一種辦法是傳遞數組的指標,這樣,複製的大小隻是一個數群組類型的指標大小。
12345678910 |
func main() {array := [5]int{1: 2, 3:4}modify(&array)fmt.Println(array)}func modify(a *[5]int){a[1] =3fmt.Println(*a)} |
這是傳遞數組的指標的例子,會發現數組被修改了。所以這種情況雖然節省了複製的記憶體,但是要謹慎使用,因為一不小心,就會修改原數組,導致不必要的問題。
這裡注意,數組的指標和指標數組是兩個概念,數組的指標是*[5]int
,指標數組是[5]*int
,注意*
的位置。
針對函數間傳遞數組的問題,比如複製問題,比如大小僵化問題,都有更好的解決辦法,這個就是切片,它更靈活,下一篇我們將詳細介紹。
《Go語言實戰》讀書筆記,未完待續,歡迎掃碼關注公眾號flysnow_org
,第一時間看後續筆記。