有經驗的程序員都知道,實際項目中很難遇到不需要存儲和讀取集合數據的情況。不論是讀寫數據庫或文件,或者訪問網絡,都需要有一種方式來處理接收和發(fā)送的數據。
Go 語言中有 3 種數據結構可以讓用戶管理集合數據:數組、切片(slice)和映射(map)。數組是切片和映射的基礎數據結構
數組
- 數組創(chuàng)建后占用一塊連續(xù)的內存
- 通過索引(也稱下標)訪問數組元素
- 數組聲明后長度和值的類型就無法修改
語法
- 創(chuàng)建數組
// 聲明格式 var 變量名 [長度]類型
var arr1 [5]int // 初始化為 [0,0,0,0,0]
var arr2 = [...]int{1, 2, 3} // 初始化為 [1, 2, 3]
arr3 := [5]int{1,2,3,4,5} // 初始化為 [1, 2, 3, 4, 5]
- 修改數組元素
通過索引下標獲取和修改數組元素
arr1[0] = 1 // arr1 [1, 0, 0, 0, 0]
arr1[4] = 5 // arr1 [1, 0, 0, 0, 5]
arr1[5] = 6 // invalid array index 5 (out of bounds for 5-element array)
- 獲取長度和容量
數組在初始化時,會指定長度,這個值可以通過len()函數獲得。數組容量可以通過cap()函數獲得。數組創(chuàng)建后長度和容量是一致的。
len(arr1)
cap(arr1)
4.遍歷
使用 for 和 range 遍歷數組
arr := [5]int{1,2,3,4,5}
for index, value := range arr {
fmt.Println("index: ", index, " value: ", value)
}
數組的內部實現
在 Go 語言里,數組是一個長度固定的數據類型,用于存儲一段具有相同類型的元素的連續(xù)塊。數組存儲的類型可以是內置類型,如整型或字符串,也可以是某種結構類型。
在圖 1-1 中可以看到數組的表示?;疑褡哟頂到M里的元素,每個元素都是緊鄰另一個元素,每個元素包含相同的類型,這個例子里是整數,并且每個元素擁有一個唯一的索引(也稱下標)來訪問。

數組是一個長度固定的數據類型,因為其占用的內存是連續(xù)分配的,CPU 能把正在使用的數據緩存更長時間。而且內存連續(xù)很容易計算索引,可以快速迭代數組里的所有元素。數組的類型信息可以提供每次訪問一個元素時需要在內存中移動的距離。既然數組的每個元素類型相同,又是連續(xù)分配,就可以以固定速度索引數組中的任意數據,速度非???。
總結
數組的特點:
- 數組創(chuàng)建后占用一塊連續(xù)的內存
- 通過索引(也稱下標)訪問數組元素
- 數組聲明后長度和值的類型就無法修改
內存連續(xù)的好處:
- CPU 能把正在使用的數據緩存更長時間
- 容易計算索引
- 快速迭代數組里的所有元素
類型固定的好處:
- 每次訪問一個元素時在內存中移動的距離固定
推論:內存連續(xù)分配,又類型固定,就可以以固定的速度索引數組中的任意數據,速度非???。
在函數間傳遞數組
根據內存和性能來看,在函數間傳遞數組是一個開銷很大的操作。在函數之間傳遞變量時,總是以值的方式傳遞的。如果這個變量是一個數組,意味著整個數組都會被完整賦值,并傳遞給函數。
為了驗證這個操作,我們來創(chuàng)建一個包含 100 萬個 int 類型元素的數組。在 64 位架構的機器上,這需要 800 萬字節(jié),即 8 MB 的內存。
var array [1e6]int
func main() {
foo(array)
}
func foo(array [1e6]int) {
fmt.Println("array length: ", len(array))
}
每次函數 foo 被調用時,都要在棧上分配 8MB 的內存。之后,整個數組的值(8MB的內存)被復制到剛分配的內存里。因此這個操作的代價是隨著數組的長度而變大。我們可以通過傳入數組的指針的方式來處理這個操作。這樣只需要復制 8 字節(jié)數據而不是 8 MB 的數據到棧上。
var array [1e6]int
func main() {
foo(&array)
}
func foo(array *[1e6]int) {
fmt.Println("array length: ", len(array))
}
這個操作會更有效地利用內存,性能也更好。不過因為現在傳遞的是指針,所以如果改變指針指向的值,會改變共享的內存。使用切片能更好地處理這類共享問題。