在 Go 語言中,數組是一種固定長度的數據結構,用于存儲相同類型的元素。以下是 Go 中數組的多種初始化方式,結合搜索結果整理如下:
(一)使用 var
關鍵字聲明并初始化數組
使用 var
關鍵字聲明數組時,可以指定數組的長度,數組的元素會被自動初始化為對應類型的零值。例如:
var arr [5]int // 聲明一個長度為5的整型數組,元素默認初始化為0
這種方式適用于需要明確數組長度且元素初始值為零值的場景。
(二)聲明時直接初始化數組
在聲明數組的同時,可以直接指定數組的元素值。例如:
var arr = [5]int{1, 2, 3, 4, 5} // 聲明并初始化一個長度為5的整型數組
這種方式適用于已知數組元素值的場景。
(三)使用短變量聲明初始化數組
通過短變量聲明 :=
可以更簡潔地初始化數組。例如:
arr := [5]int{1, 2, 3, 4, 5} // 使用短變量聲明并初始化數組
這種方式適用于函數內部或需要快速聲明和初始化數組的場景。
(四)部分初始化數組
在初始化數組時,可以只指定部分元素的值,未指定的元素會被初始化為零值。例如:
arr := [5]int{1, 2} // 初始化前兩個元素為1和2,其余為0
這種方式適用于需要部分元素初始化的場景。
(五)使用 ...
自動推斷數組長度
在初始化數組時,可以使用 ...
讓編譯器根據初始化值的個數自動推斷數組長度。例如:
arr := [...]int{1, 2, 3, 4, 5} // 自動推斷數組長度為5
這種方式適用于數組長度由初始化值決定的場景。
(六)指定索引初始化數組
在初始化數組時,可以指定某些索引位置的值,未指定的索引位置會被初始化為零值。例如:
arr := [5]int{1: 10, 3: 30} // 初始化索引1為10,索引3為30,其余為0
這種方式適用于需要指定特定索引位置值的場景。
總結
Go 語言提供了多種數組的初始化方式,包括使用 var
關鍵字、短變量聲明、部分初始化、自動推斷長度以及指定索引初始化等。這些方式可以根據實際需求靈活選擇,以滿足不同場景下的使用需求。
數組元素操作
在 Go 語言中,數組是一種固定長度、同類型的集合。我們可以對數組元素進行訪問、修改、遍歷、拷貝等操作。下面詳細介紹數組元素的常見操作方法。
一、聲明和初始化數組
1. 聲明數組
var arr [5]int // 聲明一個長度為 5 的 int 數組,默認值為 0
2. 初始化數組
arr := [3]int{1, 2, 3} // 聲明并初始化
也可以讓編譯器推斷長度:
arr := [...]int{1, 2, 3, 4} // 長度自動推斷為 4
二、訪問數組元素
通過索引訪問數組元素,索引從 0
開始:
arr := [3]int{10, 20, 30}
fmt.Println(arr[0]) // 輸出 10
fmt.Println(arr[1]) // 輸出 20
?? 注意:索引超出范圍會導致 panic:
fmt.Println(arr[3]) // panic: index out of range [3] with length 3
三、修改數組元素
通過索引直接賦值即可修改元素:
arr := [3]int{10, 20, 30}
arr[1] = 99
fmt.Println(arr) // 輸出 [10 99 30]
四、遍歷數組元素
1. 使用 for 循環
for i := 0; i < len(arr); i++ {fmt.Println(arr[i])
}
2. 使用 range(推薦)
for i, v := range arr {fmt.Printf("index: %d, value: %d\n", i, v)
}
五、數組長度
使用 len()
獲取數組長度:
arr := [3]int{1, 2, 3}
fmt.Println(len(arr)) // 輸出 3
六、數組拷貝
Go 數組是值類型,賦值時會完整拷貝整個數組:
a := [3]int{1, 2, 3}
b := a
b[0] = 99
fmt.Println(a) // [1 2 3]
fmt.Println(b) // [99 2 3]
七、多維數組
Go 支持多維數組,例如二維數組:
var matrix [2][3]int
matrix[0] = [3]int{1, 2, 3}
matrix[1] = [3]int{4, 5, 6}
fmt.Println(matrix)
// 輸出: [[1 2 3] [4 5 6]]
八、數組 vs 切片(slice)
特性 | 數組(Array) | 切片(Slice) |
---|---|---|
長度 | 固定 | 動態變化 |
傳遞方式 | 值拷貝 | 引用傳遞 |
聲明方式 | [n]T | []T |
常用場景 | 已知固定長度數據 | 動態長度、靈活操作 |
九、示例代碼匯總
package main
import "fmt"
func main() {// 聲明并初始化arr := [3]int{10, 20, 30}// 訪問元素fmt.Println(arr[0]) // 10// 修改元素arr[1] = 99fmt.Println(arr) // [10 99 30]// 遍歷for i, v := range arr {fmt.Printf("index: %d, value: %d\n", i, v)}// 數組拷貝b := arrb[0] = 100fmt.Println(arr) // [10 99 30]fmt.Println(b) // [100 99 30]
}
十、總結
- Go 數組是固定長度、同類型的集合。
- 通過索引訪問和修改元素。
- 使用
range
遍歷數組更方便。 - 數組是值類型,賦值或傳參時拷貝整個數組。
- 如需動態長度,應使用切片(slice)。
存儲不同類型的元素
在 Go 語言中,數組(array)只能存儲相同類型的元素。這是 Go 類型系統的一個基本特性,它要求數組中的所有元素必須是同一類型,以確保類型安全和內存布局的一致性。
一、為什么數組不能存儲不同類型的元素?
Go 是靜態類型語言,數組的類型由其元素類型和長度共同決定。例如:
var arr [3]int // 這是一個包含 3 個 int 類型元素的數組
如果你嘗試將不同類型的值放入數組,比如:
arr[0] = 10
arr[1] = "hello" // 編譯錯誤:cannot use "hello" (type untyped string) as type int
就會在編譯時報錯,因為類型不匹配。
二、如何實現存儲不同類型的數據?
雖然 Go 數組不能存儲不同類型,但你可以通過以下方式模擬存儲多種類型的數據:
1. 使用 interface{}
(空接口)
interface{}
是 Go 中的萬能類型,可以表示任何類型。你可以聲明一個 []interface{}
類型的切片(slice)來存儲不同類型的值:
var data []interface{} = []interface{}{42,"hello",3.14,true,
}
for _, v := range data {fmt.Println(v)
}
注意:使用 interface{}
會失去類型安全,訪問時需要類型斷言(type assertion):
val := data[1].(string) // 斷言為 string 類型
fmt.Println(val)
2. 使用結構體(struct)
如果你希望更結構化地存儲不同類型的數據,可以定義一個結構體,將不同類型作為字段:
type Item struct {ID intName stringPrice float64
}
items := [2]Item{{1, "Apple", 2.5},{2, "Banana", 1.8},
}
3. 使用聯合體(C 風格)—— 不推薦
Go 沒有像 C 語言那樣的聯合體(union),也不鼓勵使用 unsafe
包來模擬,因為會破壞類型安全。
三、數組 vs 切片 vs 接口切片
類型 | 是否可存多種類型 | 特點 |
---|---|---|
[n]T | ? 否 | 固定長度,類型必須一致 |
[]T | ? 否 | 動態長度,類型必須一致 |
[]interface{} | ? 是 | 動態長度,可存任意類型 |
四、總結
- Go 數組不能存儲不同類型,必須保持元素類型一致。
- 若需存儲多種類型,推薦使用
[]interface{}
或自定義結構體。 - 使用
interface{}
時要注意類型斷言,避免運行時錯誤。
使用數組作為函數參數示例
在 Go 語言中,數組可以作為函數參數傳遞,但由于數組是值類型,這意味著傳遞時會復制整個數組,而不是傳遞引用。下面我們詳細講解如何使用數組作為函數參數,包括傳值、傳指針、以及一些注意事項。
一、傳值方式(默認行為)
1. 基本語法
當數組作為函數參數時,默認是值傳遞,即整個數組會被復制一份。
package main
import "fmt"
func modifyArray(arr [3]int) {arr[0] = 100fmt.Println("Inside function:", arr) // [100 2 3]
}
func main() {arr := [3]int{1, 2, 3}modifyArray(arr)fmt.Println("Outside function:", arr) // [1 2 3]
}
2. 特點
- 函數內對數組的修改不會影響原數組。
- 如果數組很大,復制整個數組會影響性能。
二、傳指針方式(避免復制)
1. 基本語法
為了避免復制整個數組,可以傳遞數組的指針:
package main
import "fmt"
func modifyArrayByPointer(arr *[3]int) {(*arr)[0] = 100fmt.Println("Inside function:", *arr) // [100 2 3]
}
func main() {arr := [3]int{1, 2, 3}modifyArrayByPointer(&arr)fmt.Println("Outside function:", arr) // [100 2 3]
}
2. 特點
- 傳遞的是數組的指針,不會復制整個數組。
- 函數內對數組的修改會影響原數組。
- 適用于大數組,提升性能。
三、使用切片代替數組(推薦)
在 Go 中,更常見的做法是使用**切片(slice)**作為函數參數,因為切片是引用類型,傳遞的是底層數組的指針,不會復制整個數組。
1. 基本語法
package main
import "fmt"
func modifySlice(s []int) {s[0] = 100fmt.Println("Inside function:", s) // [100 2 3]
}
func main() {arr := [3]int{1, 2, 3}slice := arr[:] // 將數組轉換為切片modifySlice(slice)fmt.Println("Outside function:", arr) // [100 2 3]
}
2. 特點
- 切片是引用類型,傳遞的是底層數組的指針。
- 函數內對切片的修改會影響原數組。
- 適用于動態長度的數據,更靈活。
四、多維數組作為參數
多維數組也可以作為函數參數,但需要明確指定每一維的長度(除了第一維)。
1. 基本語法
package main
import "fmt"
func printMatrix(matrix [3][2]int) {for i := 0; i < 3; i++ {for j := 0; j < 2; j++ {fmt.Printf("%d ", matrix[i][j])}fmt.Println()}
}
func main() {matrix := [3][2]int{{1, 2},{3, 4},{5, 6},}printMatrix(matrix)
}
2. 特點
- 多維數組的長度必須嚴格匹配函數參數的聲明。
- 如果長度不固定,建議使用切片的切片(
[][]int
)。
五、總結
方式 | 特點 | 適用場景 |
---|---|---|
傳值 | 復制整個數組,函數內修改不影響原數組 | 小數組,不希望原數組被修改 |
傳指針 | 傳遞數組指針,函數內修改影響原數組 | 大數組,避免復制 |
傳切片 | 引用傳遞,函數內修改影響原數組 | 動態長度數據,推薦使用 |
多維數組 | 需明確指定長度,靈活性較低 | 固定維度的矩陣數據 |
六、推薦做法
- 優先使用切片:除非明確需要固定長度的數組,否則推薦使用切片。
- 大數組傳指針:如果必須使用數組且數組較大,建議傳遞指針。
- 避免多維數組:如果維度不固定,建議使用切片的切片(
[][]int
)。
數組進行排序
在 Go 語言中,可以使用標準庫中的 sort
包對數組進行排序。不過需要注意的是,Go 的 sort
包主要針對的是切片(slice),而不是數組(array)。因此,通常我們會先將數組轉換為切片,再進行排序操作。
下面我們詳細講解如何對數組進行排序,包括升序、降序、自定義排序等場景。
一、基本排序(升序)
1. 將數組轉為切片并排序
Go 的 sort.Ints
、sort.Float64s
、sort.Strings
等函數只能對切片操作,因此我們需要先將數組轉為切片。
package main
import ("fmt""sort"
)
func main() {arr := [5]int{5, 2, 6, 3, 1}slice := arr[:] // 轉為切片sort.Ints(slice) // 升序排序fmt.Println("Sorted slice:", slice) // [1 2 3 5 6]fmt.Println("Original array:", arr) // [1 2 3 5 6]
}
注意:由于切片是對底層數組的引用,排序后原數組也會被修改。
二、降序排序
Go 的 sort
包沒有直接提供降序排序的函數,但可以使用 sort.Reverse
實現降序排序。
package main
import ("fmt""sort"
)
func main() {arr := [5]int{5, 2, 6, 3, 1}slice := arr[:]sort.Sort(sort.Reverse(sort.IntSlice(slice))) // 降序排序fmt.Println("Sorted slice (desc):", slice) // [6 5 3 2 1]
}
三、自定義排序
如果數組元素是結構體,或者需要按照自定義規則排序,可以實現 sort.Interface
接口。
1. 示例:按結構體字段排序
package main
import ("fmt""sort"
)
type Person struct {Name stringAge int
}
type ByAge []Person
func (a ByAge) Len() int { return len(a) }
func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }
func main() {people := []Person{{"Alice", 30},{"Bob", 25},{"Charlie", 35},}sort.Sort(ByAge(people)) // 按 Age 升序排序fmt.Println("Sorted by age:", people)// Output: [{Bob 25} {Alice 30} {Charlie 35}]
}
四、穩定性排序
Go 的 sort
包提供了穩定排序 sort.Stable()
,保證相等元素的相對順序不變。
package main
import ("fmt""sort"
)
func main() {arr := [5]int{5, 2, 6, 2, 1}slice := arr[:]sort.Stable(sort.IntSlice(slice)) // 穩定排序fmt.Println("Stable sorted slice:", slice) // [1 2 2 5 6]
}
五、總結
排序方式 | 方法 | 適用場景 |
---|---|---|
升序排序 | sort.Ints() / sort.Float64s() / sort.Strings() | 基本類型排序 |
降序排序 | sort.Reverse(sort.IntSlice()) | 需要降序時 |
自定義排序 | 實現 sort.Interface | 結構體或復雜規則排序 |
穩定排序 | sort.Stable() | 需要保持相等元素順序 |
六、注意事項
- 數組 vs 切片:Go 的
sort
包主要針對切片,數組需要先轉為切片。 - 引用影響:切片排序會影響原數組,因為切片是對數組的引用。
- 性能考慮:對于大型數組,排序是 O(n log n) 的時間復雜度,合理使用。