1. 程式人生 > >我是陣列--就要學習Go語言

我是陣列--就要學習Go語言

前言

Go 語言給使用者提供了三種資料結構用於管理集合資料:陣列、切片(Slice)和對映(Map)。這三種資料結構是語言核心的一部分,在標準庫裡被廣泛使用。學會這些資料結構,編寫go程式會變得快速、有趣且十分靈活。掌握陣列是理解切片和對映的基礎,我們就從陣列開始學習。

什麼是陣列

Go語言中,陣列是一個長度固定的資料型別,用於儲存一段相同資料型別的元素,這些元素在記憶體中是連續儲存的。陣列儲存的型別可以是內建型別,如整型、字串等,也可以是自定義的資料結構。強調陣列固定,有別於切片,它是可以增長和收縮的動態序列。陣列的每個元素可以通過索引下標來訪問,索引下標的範圍是從[0 , len(array)-1]

宣告與初始化

陣列宣告有兩個要點:

  1. 指定陣列儲存的資料的型別;
  2. 元素個數,即陣列的長度;
var array0 [5]int   // 宣告一個包含5個元素的整型陣列,但我們並未初始化
fmt.Println(array0)  //輸出:[0 0 0 0 0]
複製程式碼

前面我們已經講過,Go 語言中宣告變數時,總會使用對應型別的零值來對變數進行初始化。陣列也不例外。 當陣列初始化時,陣列內每個元素都初始化為對應型別的零值。從輸出結果可以看到,整型數組裡的每個元素都初始化為 0,也就是整型的零值。

var array0 [5]int
array0 = [5]int{1,2,3,4,5}   //手動初始化
fmt.Println(array0) //輸出:[1 2 3 4 5] 複製程式碼

最基本的宣告並初始化:

// 宣告並初始化
var array0 = [5]int{1,2,3,4,5}
fmt.Println(array0)
複製程式碼

使用Go提供的:=操作符:

array0 := [5]int{1,2,3,4,5}
複製程式碼

Go提供了一種機制,免去了我們指定陣列長度的煩惱,使用...,根據初始化時陣列元素的數量來確定該陣列的長度。

array := [...]int{1,2,3,4,5}
fmt.Println(len(array))  // 內建的len()函式返回陣列中元素的個數。
複製程式碼

假如我只想給索引為1、3的元素指定初始化的值怎麼辦?還是有辦法的:

array := [...]int{0,2,0,4,0}
複製程式碼

更簡便的方法:

array := [5]int{1:2,3:4}
複製程式碼

學會使用陣列

上面提到過得,因為陣列的記憶體分佈是連續的,所以在陣列訪問任一的效率是很高,這也是陣列的優勢。可以使用[]運算子訪問陣列的當個元素。

array := [5]int{1,2,3,4,5}
fmt.Println(array[3])    //訪問單個元素
array[3] = 30            //修改當個元素的值
fmt.Println(array[3])
複製程式碼

使用forfor range迴圈遍歷陣列:

array := [5]int{1,2,3,4,5}
// for
for i:=0;i<len(array);i++ {
	fmt.Printf("索引%d的值: %d\n",i,array[i])
}	
// for range
for i,v := range array{
	fmt.Printf("索引%d的值: %d\n",i,v)
}
	
複製程式碼

輸出的結果是一樣的:

索引0的值: 1
索引1的值: 2
索引2的值: 3
索引3的值: 4
索引4的值: 5
複製程式碼

陣列變數的型別包括陣列長度每個元素的型別。Go語言規定只有這兩部分都相同的陣列,才是型別相同的陣列,才能互相賦值,不然會編譯出錯。

var array1 [5]int
array2 := [5]int{1,2,3,4,5}
array1 = array2
fmt.Println(array1)   // 輸出:[1 2 3 4 5]

var array3 [4]int = array2  
// 編譯出錯:cannot use array2 (type [5]int) as type [4]int in assignment
複製程式碼
陣列指標和指標陣列

我們可以宣告一個指標變數,指向一個數組:

arr := [6]int{5:9}
// 陣列指標
var ptr *[6]int = &arr
// 簡寫 
ptr := &arr
複製程式碼

需要注意的是,指標變數ptr的型別是*[6]int,也就是說它只能指向包含6個元素的整型陣列,否則編譯報錯。 指標陣列和陣列差不多,只不過元素型別是指標:

// 指標陣列
x,y := 1,2
var arrPtr = [5]*int{1:&x,3:&y}  // 沒有手動初始化的元素,已經自動初始化指標型別對應的零值 nil
fmt.Println(*arrPtr[1])   // 輸出:1

*arrPtr[1] = 10     
fmt.Println(x,*arrPtr[1])  // 輸出:10 10
複製程式碼

*arrPtr[1] = 10,同時也修改了變數x的值,因為xarrPtr[1]指向同一記憶體地址。 提一點,相同型別的指標陣列也可以相互賦值。 總結一句話:注意*與誰結合,如p *[5]int*與陣列結合說明是陣列指標;如p [5]*int*int結合,說明這個陣列都是int型別的指標,是指標陣列。

函式間傳遞陣列

函式之間傳遞變數時, 總是以值的方式傳遞的。如果變數是一個數組,意味著整個陣列,不管有多大,都會完整賦值一份,並傳遞給函式。複製出來的陣列只是原陣列的一份副本,在函式中修改傳遞進來陣列是不會改變原陣列的值得。

// 傳遞陣列的副本
func modify(a [5]int)  {
	a[1] = 1
	fmt.Println(a)
}

func main(){
	arr := [5]int{4:9}
	fmt.Println(arr)
	modify(arr)
	fmt.Println(arr)
}
複製程式碼

輸出:

[0 0 0 0 9]
[0 1 0 0 9]
[0 0 0 0 9]
複製程式碼

原陣列元素的值沒有被修改。

大家可以想一個問題,如果一個數組的資料量很大,如果還採用值傳遞的話,這無疑是一個開銷很大的操作,對記憶體和效能都是不友好的。還好,我們還有一個更好的辦法:傳遞指向陣列的指標,這樣只需要複製一個數組型別的指標大小就可以。

// 傳遞陣列的指標
func modifyPtr(a *[5]int){
	a[1] = 2
	fmt.Println(*a)
}

func main(){
	arr := [5]int{4:9}
	fmt.Println(arr)
	modifyPtr(&arr)
	fmt.Println(arr)
}
複製程式碼

輸出:

[0 0 0 0 9]
[0 2 0 0 9]
[0 2 0 0 9]
複製程式碼

有沒有發現!原陣列已經改變了,因為現在傳遞的是陣列指標, 所以如果改變指標指向的值,原陣列在記憶體的值也會被修改。這種操作雖然更有效地利用記憶體(免去了大量的記憶體複製)、效能也更好,但如果沒有處理好指標,也會帶來不必要的問題,所以,使用的時候需要謹慎小心。

下一節,我們來講講切片!

關注公眾號「Golang來了」,獲取最新文章!

公眾號二維碼