Go通關04:正確使用 array、slice 和 map!

微客鳥窩發表於2021-08-03

Array(陣列)

陣列存放的是固定長度、相同型別的資料。

陣列宣告

  1. var <陣列名> = [<長度>]<元素>{元素1,元素2}
    var arr = [2]int{1,2}
    或者
    arr := [2]int{1,2}

  2. var <陣列名> = [...]<元素型別>{元素1,元素2}
    var arr = [...]int{1,2}
    或者
    arr := [...]int{1,2}

  3. var <陣列名> = [...]<型別>{索引1:元素1,索引2:元素2}
    var arr = [...]int{1:1,0:2}
    或者
    arr := [...]int{1:1,0:2}

陣列的每個元素在記憶體中都是連續存放的,每個元素都有一個下標,下標從0開始。
陣列長度可以省略,會自動根據{}中的元素來進行推導。
沒有初始化的索引,預設值是陣列型別的零值。

陣列迴圈

for i,v := range array {
  fmt.Printf("索引:%d,值:%s\n",i,v)
}
  1. range 表示式返回陣列索引賦值給 i,返回陣列值賦值給 v。
  2. 如果返回的值用不到,可以用 _ 下劃線丟棄:
for _,v:= range array{
  fmt.Printf("值:%s\n",i,v)
}

切片

切片和陣列型別,可以理解為動態的陣列,切片是基於陣列實現的,它的底層就是一個陣列。對於陣列的分割,便可以得到一個切片。

陣列生成切片

slice := array[start:end]

array := [5]string{"a","b","c","d","e"}
slice := array[2:5]
fmt.Println(slice) //[c d e]

注意:這裡包含索引2,但是不包含索引5的元素,即:左閉右開。
經過切片後,切片的索引範圍也改變了。
array[start:end] 中的 start 和 end 都是可以省略的,start 的預設值是 0 ,end 的預設值為陣列的長度。

array[:] 等價於 array[0:5]

切片修改

切片的值也可以被修改,這裡也可以證明切片的底層是陣列。

array := [5]string{"a","b","c","d","e"}
slice := array[2:5] //[c d e]
slice[1] = "f"
fmt.Println(slice) //[c f e]
fmt.Println(array) //[a b c f e]

修改切片,對應的陣列值也被修改了,所以證明基於陣列的切片,使用的底層陣列還是原來的陣列,一旦修改切片的元素值,底層陣列對應的值也會被修改。

切片宣告

使用 make 函式宣告切片

//宣告一個元素型別為string的切片,長度是4
slice := make([]string,4)
//長度是4,容量是8
slice1 := make([]srting,4,8)

切片的容量不能比切片長度小。
長度就是元素個數。
容量就是切片的空間。

上面例項在記憶體上劃分了一個容量為8的記憶體空間,但是隻是用了4個記憶體空間,剩餘的處於空閒狀態。當通過 append 往切片追加元素時,會追加到空閒記憶體上,剩餘空間不足時,會進行擴容。

字面量初始化切片

slice2 := []string{"a","b","c"}
fmt.Println(len(slice2),cap(slice2)) //3 3

Append

append 函式對一個切片進行追加元素:

slice3 := append(slice2,"d")
//追加多個元素
slice3 := append(slice2,"d","f")
//追加一個切片
slice3 := append(slice2,slice...)

小技巧:
在建立新切片時,最好讓長度和容量一樣,這樣追加操作的時候就會生成新的底層陣列,從而和原有陣列分離,就不會因為公用底層陣列導致修改內容的時候影響多個切片。

切片迴圈

切片迴圈與陣列一樣,也是使用 for range 方式。

Map (對映)

map 是一個無序的 k-v 鍵值對集合。其中 k 必須是相同型別。k 和 v 的型別可以不同。 k 的型別必須支援 == 比較運算子,這樣才可以判斷它是否存在,並保證唯一

Map 宣告初始化

  1. make:
    mapName := make(map[string]int)

  2. 字面量:
    mapName := map[string]int{"無塵":29}

如果不想建立的時候新增鍵值對,使用空大括號{}即可,切記不能省略。

Map 獲取、刪除

//新增鍵值對或更新對應的key的value
mapName["無塵"] = 20
//獲取指定key的value
age := mapName["無塵"]

獲取不存在的 k-v 鍵值對時,如果 key 不存在,返回的 value 是該值的零值,所以很多時候,需要先判斷 map 中的 key 是否存在。

nameAge := make([string]int)
nameAge["無塵"]=29
age,ok := nameAge["無塵"]
if ok {
  fmt.Println(age)
}
  • map 的 [] 操作返回兩個值
    • 第一個是 value
    • 第二個是標記該 key 是否存在,存在則為 true

delete()函式進行刪除

delete(nameAge,"無塵")

  • delete 有兩個引數,一個是map,一個是要刪除的 key 。

遍歷 Map

nameAge["無塵"] = 29
nameAge["無塵1"] = 30
nameAge["無塵2"] = 31

for k,v := range nameAge{
  fmt.Println("key is",k,"value is ",v)
}
  • 對應 map ,for range 返回兩個引數,分別是 k 和 v。

小技巧:for range 遍歷 map 的時候,若使用一個返回值,則這個返回值是 map 的 key 。

Map 的大小

map 不同於切片,map 只有長度,沒有容量。可以使用 len 函式獲取 map 大小。

String 和 []byte

字串也是一個不可變的位元組序列,可以直接轉為位元組切片 []byte :

s:="Hello無塵小生"
bs := []byte(s)

string 不止可以直接轉為 []byte,還可以使用 [] 操作符獲取指定索引的位元組值。

字串是位元組序列,每一個索引對應一個位元組,在 UTF8 編碼下,一個漢字對應三個位元組。
如果把一個漢字當做一個長度計算,可以使用 utf8.RuneCountInString 函式。
for range 遍歷時,是按照 unicode 字元進行迴圈的,一個漢字佔一個長度。

相關文章