顧名思義,復合數據類型就是由其他類型組合而成的類型。Go語言基本的復合數據類型有:指針、數組、切片、字典(map)、通道、結構和接口
一、數組(Array)
1.1 什么是數組?
Go語言提供了數組類型的數據結構。
數組是具有相同唯一類型的一組編號且長度固定的數據項序列,這種類型可以是任意的原始類型,例如:整形、字符串或者自定義類型。
數組元素可以通過索引(位置)來讀取(或者修改),索引從0開始,第一個元素索引為
0,第二個索引為1,以此類推,數組的下標取值范圍是從0開始,到長度減1。
1.2 數組的特點
- 數組創建完長度就固定了,不可以再追加元素。
- 數組是值類型的,數據賦值或作為函數參數都是值拷貝。
- 數組長度是數組類型的組成部分,10[int]和20[int]表示不同的類型。
- 可以根據數組創建切片。
1.3 數組的語法
數組語法:
//語法:
var variable_name[size] variable_type
var 數組名 [長度] 數據類型
var 數組名 = [長度] 數據類型{元素1,元素2...}
數組名 := [...]數據類型{元素1...}
注:需要指明數組的大小和存儲的數據類型。
示例代碼:
var array [10] int //聲明一個有10個整型的數組,元素默認值為0,此種方法很少使用
array := [...]float64{7.0,8.5,9.1,2.0,50.0} //[...]后面跟字面量初始化列表
初始化數組中{}中的元素個數不能大於[]中的數字。
如果忽略[]中的數字不設置數組大小,Go語言會根據元素的個數來設置數組的大小:
var array = []int {1,2,3,4,5}
arr[4] = 5
數組的其它創建方式:
array := [4]int{1,50,100,150} //指定長度和初始化字面量
array := [...]int{1,50,100,150} //不指定長度,但是由后面的初始化列表數量來確定其長度
array := [4]int{1:1,3:100} //指定總長度,並通過索引值進行初始化,沒有初始化元素時,使用類型默認值
array := [...]int{1:1,3:100} //不指定總長度,通過索引值進行初始化,數組長度由最后一個索引值確定,沒有指定索引的元素被初始化為類型的零值
數組訪問:
- 數組通過下標訪問,也叫索引 index,可理解為:數組中存儲這個元素的位置
- 默認從0開始的整數,每次累加1,直到長度減1
- 訪問數組語法:
數組名[index]
- 訪問數組時,不能越界,取值范圍為:[0,長度-1]
- 長度和容量
len() 取數組長度,(數組中實際存儲的數據量),其它還可用於array/map/slice/string
cap() 取數組容量,(數組中能夠存儲的數據量)
用法舉例:
len(數組名)
fmt.Println(len(數組名))
cap(數組名)
fmt.Println(cap(數組名))
1.4 數組的內存
GO語言數組有一個16進制的首地址(內存地址)使用%p可以查看!
fmt.Printf("%p\n", &數組名)
1.5 數組的遍歷
遍歷數組:
package main
import "fmt"
func main() {
array := [...]string{"比特幣","以太坊","泰達幣","SEA","門羅幣"}
for i :=0;i < len(array);i++ {
}
fmt.Println(array)
}
使用range遍歷數組
package main
import "fmt"
func main() {
array := [...]string{"比特幣","以太坊","泰達幣","SEA","門羅幣"}
for i,v := range array{
fmt.Printf("下標是:%v,數值是:%v\n",i,v)
}
}
如果只需要值並希望忽略索引,那么可以通過_blank標識符替換索引來實現這一點。
package main
import "fmt"
func main() {
array := [...]string{"比特幣","以太坊","泰達幣","SEA","門羅幣"}
for _,value := range array{
fmt.Printf("數值是:%v\n",value)
}
}
1.6 數組是值類型
在 Golang 中,數組是值類型,這意味着數組也可以用在賦值操作中。變量名代表整個數組,同類型的數組可以賦值給另一個數組:
var arr1 [3]string
arr2 := [3]string{"nick", "jack", "mark"}
// 把 arr2 的賦值(其實本質上是復制)到 arr1
arr1 = arr2
復制完成后兩個數組的值完全一樣,但是彼此之間沒有任何關系:
前面我們不止一次地提到:數組的類型包括數組的長度和數組元素的類型。只有這兩部分都一樣才是相同類型的數組,也才能夠互相賦值。下面的代碼中,在類型不同的數組間賦值,編譯器會阻止這樣的操作並報錯:
// 聲明第一個包含 4 個元素的字符串數組
var arr1 [4]string
// 聲明第二個包含 3 個元素的字符串數組,並初始化
arr2 := [3]string{"golang", "java", "python"}
// 將 arr2 賦值給 arr1
arr1 = arr2
編譯以上代碼時,會報錯如下:
cannot use arr2 (type [3]string) as type [4]string in assignment
//編譯器表示在賦值時不能把 type [3]string 當 type [4]string 用。
把數組賦值給其它數組時,實際上是完整地復制一個數組。所以,如果數組是一個指針型的數組,那么復制的將是指針,而不會復制指針所指向的對象。看下面的代碼:
1.7 數組的排序
讓數組中的元素具有一定順序
array := [...]int{15,23,8,10,7}
升序: array := [...]int{7,8,10,15,23}
降序: array := [...]int{23,15,10,8,7}
排序算法:
冒泡排序、插入排序、選擇排序、希爾排序、堆排序、快速排序
冒泡排序:(Bubbo Sort)
依次比較兩個相鄰的元素,如果它們的順序(如從小到大)就把它們的位置進行交換
示例:
func main() {
array := [...]int{15,23,8,10,7}
for i := 1;i<len(array);i++ {
for j :=0;j <len(array)-i;j++ {
if array[j] > array[j+1]{
array[j],array[j+1] = array[j+1],array[j]
}
}
fmt.Println(array)
}
}
1.8 多維數組
Go語言支持多維數組,以下為常用的多維數組聲明語法方式:
#常用的多維數組聲明方式:
var variable_name[size] [size]...[size]variable_type
#以下實例聲明了三維的整型數組:
var threedim [5][10][4]int
多維數組可通過大括號來初始值。以下 聲明二維數組的示例代碼 :
// 聲明一個二維整型數組,兩個維度分別存儲 4 個元素和 2 個元素
var arr [4][2]int
// 使用數組字面量來聲明並初始化一個二維整型數組
arr1 := [4][2]int{{10, 11}, {20, 21}, {30, 31}, {40, 41}}
// 聲明並初始化外層數組中索引為 1 和 3 的元素
arr2 := [4][2]int{1: {20, 21}, 3: {40, 41}}
// 聲明並初始化外層數組和內層數組的單個元素
arr3 := [4][2]int{1: {0: 20}, 3: {1: 41}}
下圖展示了上面代碼聲明的二維數組在每次聲明並初始化后包含的值:
訪問二維數組
二維數組通過指定坐標來訪問。如數組中的行索引與列索引,例如:
int val = a[2][3]
此示例訪問了二維數組 val 第三行的第四個元素。
1.9 總結
數組在 Golang 中是作為高性能的基礎類型設計的,因此對用戶來說使用起來並不是特別方便,這一點在眾多的開源代碼中(數組用的少,slice 用的多)可以得到印證。其實基於數組實現的 slice 以其簡單靈活的特性更易於被大家接受,這也正是 Golang 設計 slice 的初衷。
數組相對於切片的特點:
- 數組是值對象,可以進行比較,可以將數組用作 map 的映射鍵。而這些,切片都不可以,不能比較,無法作為 map 的映射鍵。
- 數組有編譯安全的檢查,可以在早起就避免越界行為。切片是在運行時會出現越界的 panic,階段不同。
- 數組可以更好地控制內存布局,若拿切片替換,會發現不能直接在帶有切片的結構中分配空間,數組可以。
- 數組在訪問單個元素時,性能比切片好。
- 數組的長度,是類型的一部分。在特定場景下具有一定的意義。
- 數組是切片的基礎,每個數組都可以是一個切片,但並非每個切片都可以是一個數組。如果值是固定大小,可以通過使用數組來獲得較小的性能提升(至少節省 slice 頭占用的空間)。
1.10 數組練習題
- 求數組[1, 3, 5, 7, 8]所有元素的和
方法一:
package main
import "fmt"
func main() {
arr := [5]int{1,3,5,7,8}
sum := 0
for i := 0;i < 5;i++{
sum += arr[i]
}
fmt.Println(sum)
}
方法二:
package main
import "fmt"
func main() {
arr := [...]int{1,3,5,7,8}
sum := 0
for _,i := range arr{ //range類似迭代器,可以遍歷數組,字符串,map等等
sum += i
}
fmt.Println(sum)
}
- 創建一個隨機種子數並隨機生成10個100以內的int類型元素,進行冒泡排序
方法1:
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
rand.Seed(time.Now().UnixNano())
var arr [10]int
for i := 0; i <= 9; i++ {
num := rand.Intn(100)
arr[i] = num
}
length := len(arr) - 1
for i := length; i >= 0; i-- {
for j := 0; j <= i - 1; j++ {
if arr[j] > arr[j+1] {
arr[j], arr[j+1] = arr[j+1], arr[j]
}
}
}
fmt.Println("排序后",arr)
}
方法2:
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
rand.Seed(time.Now().UnixNano())
var arr [10]int
for i := 0;i<10;i++ {
num := rand.Intn(100)
arr[i] = num
}
fmt.Println("隨機數組:",arr)
for i := 1;i <len(arr);i++{
for j :=0;j < len(arr) - i;j++ {
if arr[j] > arr[j+1] {
arr[j],arr[j+1] = arr[j+1],arr[j]
}
}
}
fmt.Println("排序后數組:",arr)
}