本章會介紹的是Go語言中較為複雜一點的型別 :
最基本的集合形式,它的定義方式如下:
var arr [n]type // n為陣列長度
陣列元素可以是任意型別,但只能是一種型別。
此外,宣告陣列時必須指定長度
如果沒指定長度,反而會變成切片(slice),切片是一種操作彈性更大的的集合形式。
範例:
var arr [10]int // 宣告了一個 int 型別的陣列
arr[0] = 10 // 陣列是從 0 開始的
arr[1] = 11 // 賦值
2.0.1 如果要在宣告時為陣列給予初值 :
var arr [n]type{<value1>, <value2>, <value3>,...<valueN>}
例如:
[5]int{1} //如此一來就宣告了一個陣列且第一個元素為1,其餘四個為0值
2.0.2 根據提供的初始值數量來設定陣列長度 :
用三個點...表示。
var arr [...]type{<value1>, <value2>, <value3>,...<valueN>}
例如:
var arr [...]int{1,2,3,4,5} //宣告了一個長度為5的陣列
不管你用哪一種宣告方式,陣列的長度會在最一開始就決定!!
2.0.3 透過索引鍵賦值
var arr [...]type{<索引鍵1> : <value1>, <索引鍵2> : <value2>, <索引鍵3> : <value3>,...<索引鍵N> : <valueN>}
例如 :
package main
import "fmt"
func main() {
var arr1 [10]int
var arr2 = [...]int{9:0}
var arr3 = [10]int{1, 9: 10, 4: 5}
fmt.Println(arr1)
fmt.Println(arr2)
fmt.Println(arr3)
}
結果
[0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0]
[1 0 0 0 5 0 0 0 0 10]
2.0.4 讀取陣列元素
package main
import "fmt"
func main() {
// 創建一個整數陣列
numbers := [5]int{1, 2, 3, 4, 5}
// 讀取陣列中的元素
// 陣列索引從 0 開始,所以第一個元素是 numbers[0]
firstElement := numbers[0]
// 第二個元素是 numbers[1]
secondElement := numbers[1]
// 以此類推
thirdElement := numbers[2]
fourthElement := numbers[3]
fifthElement := numbers[4]
// 輸出讀取的元素值
fmt.Println("第一個元素:", firstElement) // 輸出:第一個元素: 1
fmt.Println("第二個元素:", secondElement) // 輸出:第二個元素: 2
fmt.Println("第三個元素:", thirdElement) // 輸出:第三個元素: 3
fmt.Println("第四個元素:", fourthElement) // 輸出:第四個元素: 4
fmt.Println("第五個元素:", fifthElement) // 輸出:第五個元素: 5
}
2.0.5 將值寫入陣列
package main
import "fmt"
func main() {
// 創建一個整數陣列
numbers := [5]int{}
// 將值寫入陣列
numbers[0] = 1
numbers[1] = 2
numbers[2] = 3
numbers[3] = 4
numbers[4] = 5
// 輸出陣列的內容
fmt.Println("陣列的內容:", numbers) // 輸出:陣列的內容: [1 2 3 4 5]
}
2.0.6 走訪陣列
for i := 0; i < len(<陣列>); i++ {
// 存取<陣列>[i]
}
範例:
package main
import "fmt"
func main() {
// 創建一個整數陣列
numbers := [5]int{1, 2, 3, 4, 5}
// 使用 for 迴圈走訪陣列
for i := 0; i < len(numbers); i++ {
fmt.Printf("索引 %d 的元素值是 %d\n", i, numbers[i])
}
// 使用 range 關鍵字走訪陣列
for index, value := range numbers {
fmt.Printf("索引 %d 的元素值是 %d\n", index, value)
}
}
Note len()效率: 我們不需要去擔心Go每次執行len()時都需要重新計算陣列大小,因為陣列的長度早就是陣列型別定義的一部分,Go語言平時也會追蹤他們元素的數量
陣列雖然方便但硬性規定宣告時必須要長度,只能處理特定長度,在很多應用場景中,陣列並不能滿足我們的需求。在初始定義陣列時,我們不確定需要多大的陣列,所以我們就需要“動態陣列”。在 Go 裡面這種資料結構叫slice,也就是希望兼顧陣列的便利性,與不受長度限制。
切片也可以使用append()去新增切片元素。
2.1.1 使用切片
// 和宣告 array 一樣,只是少了長度
var iAmSlice []int
例如:
slice := []byte {'a', 'b', 'c', 'd'}
slice可以從一個陣列或一個已經存在的 slice 中再次宣告。
slice透過 array[i:j] 來取得,其中 i 是陣列的開始位置,j是結束位置,但不包含array[j],它的長度是j-i。
package main
import "fmt"
func main() {
// 建立一個整數數組
numbers := [5]int{1, 2, 3, 4, 5}
// 從陣列建立一個切片
// 切片包含陣列的第二個到第四個元素,不包含 numbers[4]
slice1 := numbers[1:4]
// 列印切片的內容
fmt.Println("切片1的內容:", slice1) // 輸出:切片1的內容: [2 3 4]
// 修改切片的元素會影響原始數組
slice1[0] = 99
fmt.Println("修改後的切片1的內容:", slice1) // 輸出:修改後的切片1的內容: [99 3 4]
fmt.Println("原始陣列的內容:", numbers) // 輸出:原始陣列的內容: [1 99 3 4 5]
// 從現有切片建立一個新的切片
// 新切片包含切片1的第一個到第三個元素,不包含 slice1[2]
slice2 := slice1[0:2]
// 列印新切片的內容
fmt.Println("切片2的內容:", slice2) // 輸出:切片2的內容: [99 3]
// 修改新切片的元素也會影響原始陣列和原始切片
slice2[1] = 88
fmt.Println("修改後的切片2的內容:", slice2) // 輸出:修改後的切片2的內容: [99 88]
fmt.Println("修改後的切片1的內容:", slice1) // 輸出:修改後的切片1的內容: [99 88 4]
fmt.Println("原始陣列的內容:", numbers) // 輸出:原始陣列的內容: [1 99 88 4 5]
}
slice 有一些簡便的操作:
2.1.2 為切片加入多個新元素與解包算符(unpack operator)
將兩個切片接起來。
package main
import "fmt"
func main() {
// 建立一個整數切片
numbers := []int{1, 2, 3}
// 在切片中新增一個新元素
numbers = append(numbers, 4)
// 列印切片內容
fmt.Println("切片內容:", numbers) // 輸出:切片內容: [1 2 3 4]
// 在切片中新增多個新元素
newElements := []int{5, 6, 7}
numbers = append(numbers, newElements...)
// 列印切片內容
fmt.Println("切片內容:", numbers) // 輸出:切片內容: [1 2 3 4 5 6 7]
}
2.2.3 切片的工作原理
切片的內部結構可以概括為包含三個元素(隱藏屬性):
指標(Pointer):指向底層陣列的第一個元素。
長度(Length):切片中的元素數量,即切片的實際長度。
最大長度(Capacity):切片從其起始位置到底層陣列的末端的最大長度。
這個內部結構使切片非常靈活,因為它們可以動態增長,而不需要複製整個陣列。 當切片的長度超過其最大長度時,會建立一個新的隱藏陣列,並將元素複製到新的數組中。
當我們使用 append() 函數將一個新的元素加入切片時,可能會發生以下情況:
如果切片的容量還有剩餘(切片的長度小於容量),新的元素將直接加入到隱藏陣列中,並更新切片的長度。
如果切片的容量已經滿了,Go 將會建立一個更大的新隱藏陣列,然後將舊數組中的元素複製到新隱藏陣列中,並將新元素新增到新隱藏陣列中。 接著,切片的引用將更新為新的底隱藏陣列,同時也會更新切片的長度。 需要注意的是,切片的起始位置可能會隨著底層陣列的更換而改變。
切片也會記住它在原始隱藏陣列中的起始位置,所以如果一個切片與原始數組具有相同的長度,它將引用隱藏陣列的全部元素。 這使得切片非常適合在處理動態大小的資料集時,因為它們可以自動擴展隱藏陣列以適應更多的元素。
用 make 控制切片容量
使用 make 函數可以建立指定長度和容量的切片。** make 接受三個參數:切片的類型、長度和容量。** 容量是一個可選參數,如果省略,切片的容量將與長度相同。
package main
import "fmt"
func main() {
// 建立一個長度為3,容量為5的整數切片
slice1 := make([]int, 3, 5)
// 列印切片的長度和容量
fmt.Printf("切片1的長度:%d,容量:%d\n", len(slice1), cap(slice1))
// 新增元素到切片
slice1[0] = 1
slice1[1] = 2
slice1[2] = 3
// 列印切片的內容
fmt.Println("切片1的內容:", slice1)
// 使用 append 函數為切片新增元素
slice1 = append(slice1, 4, 5)
// 列印切片的長度和容量
fmt.Printf("切片1的長度:%d,容量:%d\n", len(slice1), cap(slice1))
// 列印切片的內容
fmt.Println("切片1的內容:", slice1)
}
輸出結果:
切片1的長度:3,容量:5
切片1的內容: [1 2 3]
切片1的長度:5,容量:5
切片1的內容: [1 2 3 4 5]
slice 幾個內建函式 :
**2.3 映射表 map **
Go語言的map是一種雜湊表(hashmap),具備key與value,鍵值對。
map [keyType] valueType
建立、讀取、寫入map範例:
package main
import "fmt"
func main() {
// 建立一個空的map,鍵是字串,值是整數
var ages map[string]int
// 使用make函數初始化map
ages = make(map[string]int)
// 新增鍵值對到map
ages["Alice"] = 25
ages["Bob"] = 30
ages["Charlie"] = 35
// 存取map中的值
aliceAge := ages["Alice"]
fmt.Println("Alice的年齡是:", aliceAge)
// 刪除map中的鍵值對
delete(ages, "Bob")
// 檢查map中是否存在某個鍵
if _, exists := ages["Bob"]; !exists {
fmt.Println("Bob不存在於map")
}
// 遍歷map的所有鍵值對
for name, age := range ages {
fmt.Printf("%s的年齡是:%d\n", name, age)
}
}
輸出結果:
Alice的年齡是: 25
Bob不存在於map
Alice的年齡是:25
Charlie的年齡是:35
說明一下:
由於Go不會幫你初始化map鍵值(map的零值就是nil),必須在定義map時一併附於鍵值:
map [keyType] valueType {<key1>:<value1>,<key2>:<value2>,<key3>:<value3>,...<keyN>:<valueN>}
如果嘗試對一個未經初始化的map賦值,就會引發執行期間panic,所以一定要避免定義一個零值map。
2.3.1 初始化
更常用的方式是使用make()函示回傳一個經過初始化的map,但make()在此傳入的參數會與初始化切片時有所不同。
make(map [keyType] valueType , capacity)
Go不會替map建立索引鍵,所以沒辦法像在slice使用make()時給他指定長度,有不能用cap()來查詢map容量。
2.3.2 初始化後加入元素
<map名稱>[<索引鍵>] = <值>
從map讀取元素
在Go語言中,當你嘗試透過鍵在map中取得值時,會傳回兩個值:值本身和一個存在狀態(true表示存在,false表示不存在)。 這種方式可以幫助你判斷鍵是否存在,避免了嘗試取得不存在的鍵而導致的運行時錯誤。
當然也是可以先檢查零值來判斷索引鍵是否存在,但這樣無法永遠擔保零值就表示查無此鍵(零值有可能是其他意義的資料,這取決於具體的資料類型和上下文)。
因此,為了準確判斷鍵是否存在,最好還是使用存在狀態來檢查,而不是只依賴零值。這種方式更加可靠和安全。
在判斷鍵是否存在時,可以使用_, exists := mapName[key]的方式,確保了你可以明確知道鍵是否存在,而不會受到零值的影響。
<值>, <存在狀態> := <map名稱>[<索引值>]
package main
import "fmt"
func main() {
// 建立一個範例 map,鍵為字串,值為整數
ages := map[string]int{
"Alice": 25,
"Bob": 30,
"Charlie": 35,
}
// 要檢查的鍵
keyToCheck := "Bob"
// 使用存在狀態來檢查鍵是否存在
value, exists := ages[keyToCheck]
// 判斷鍵是否存在
if exists {
fmt.Printf("%s 的年齡是 %d 歲\n", keyToCheck, value)
} else {
fmt.Printf("%s 不在 map 中\n", keyToCheck)
}
// 另一個例子,檢查一個不存在的鍵
keyToCheck = "David"
value, exists = ages[keyToCheck]
// 判斷鍵是否存在
if exists {
fmt.Printf("%s 的年齡是 %d 歲\n", keyToCheck, value)
} else {
fmt.Printf("%s 不在 map 中\n", keyToCheck)
}
}
以上就是複合型別的部分整理,關於slice底層運作還有一些東西需要說明,先交了之後再補充,要沒時間了牙敗~~