Go學習筆記(9)Go容器類型——數組&切片&map

寫在前面

    前面的文章介紹了Go的一些基本類型,本文開始涉及Go的一些容器類型,它們都是可以包含多個元素的數據結構,如數組、切片、map

數組

    數組是具有相同類型且長度固定的一組元素集合,定義的格式:var 數組名 [數組長度]數組元素類型,下面聲明一個長度爲5的int型數組arr

var arr [5]int
  • 數組元素可以是任意的原始類型,例如整型、字符串等,一個數組內所有元素的類型必須是相同的
  • 數組的長度必須是一個常量表達式,且是一個非負整數,特別注意數組的長度也是數組類型的一部分,因此不同長度的數組是不同的類型。數組的長度在聲明時就要給出。獲取數組長度用內置函數len(arr)
  • 當聲明數組時所有的元素都會被初始化爲默認的類型零值,我們也可以在聲明數組的同時進行初始化
    var arr = [5]int{1, 2, 3, 4, 5}
    a := [3]int{1, 2, 3}
    
    還可以在初始化時僅初始化指定元素
    b := [3]int{1 : 2, 2 : 3}  //初始化索引爲1的值爲2,索引爲2的值爲3
    
  • 如果聲明數組時不想直接寫長度,可以用...代替,編譯器會自動生成滿足最低長度要求的數組
    c := [...]int{4 : 1}        //因爲我們指定了索引爲4的值爲1,因此數值至少包含5個元素,這裏將生成長度爲5的數組
    
  • 通過for循環來遍歷操作數組
    var arr [5]int
    for i := 0; i < len(arr); i++ {
    	arr[i] = i * 2
    }
    for i, v := range arr {
    	fmt.Println("index: ", i, "value: ", v)
    }
    
  • 數組是一種值類型(不像C/C++那樣是指向首元素的指針),可以通過new來創建數組,不過它返回的是指向該數組的指針
    d := new([4]int)
    fmt.Println(d)            //&[0,0,0,0]
    fmt.Println(*d)           //[0,0,0,0]
    
    注意區分指向數組的指針和指針數組
    e := [5]int{1, 2, 3, 4, 5}
    var p *[5]int = &e              //p爲指向數組e的指針 &[1,2,3,4,5]
    x, y := 1, 2
    z := [2]*int{&x,&y}            //z是一個指針數組
    
  • 多維數組
    h := [2][3]int{
    	{1, 1, 1},
    	{2, 2, 2}
    }
    
  • 數組可以使用==!=來比較
    f := [2]int{1, 2}
    g := [2]int{1, 2}
    fmt.Println(f == g)                 //true
    

切片

    切片(slice)是對某個數組的一段連續片段的引用(該數組我們稱爲相關數組,該片段可以是整個數組,也可以是數組中的某一段),因此切片是一個引用類型。切片底層實現是數組,它與數組的關係如下圖:
在這裏插入圖片描述
    多個slice可以指向同一個底層相關數組,此時其中一個值改變會影響全部的值

  • 切片是一個可變長的數組,它的長度可以動態修改,可以用len()來獲取切片的長度。切片還有另外一個屬性容量,表示切片可以達到的最大長度,可以用cap()來獲取。切片的容量等於切片的長度+相關數組中在切片之後剩下的的長度。所以切片的長度小於等於切片的容量。
  • 切片的容量是預先分配的,如果在運行的過程中,切片的長度超過了原來分配的容量,則會重新分配一個空間更大的數組,然後將值都拷貝過去
  • 切片的聲明格式:var 切片名 []元素類型,這裏不需要指定長度,如果是數組則必須指定長度或者用...替代
  • 切片可以通過底層相關數組切取,也可以用make直接創建,還可以通過已有的切片來生成
    • 通過數組生成
      arr := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
      s1 := arr[2:5]           //切取數組arr索引從2~4這一段(即[3,4,5]),注意索引值不包括5
      s2 := arr[:]               //切片爲整個arr數組
      s3 := arr[5:]             //切取從索引5開始到末尾,即[6,7,8,9,10]
      
    • 通過make創建
      格式:make([]type, len, cap),其中cap可以省略,省略則默認和len相同
      s4 := make([]int, 3, 20)
      s5 := make([]int, 3)
      
    • 通過切片生成,這種方式有時也被稱之未稱未切片重組reslice
      sa := []int{1, 2, 3, 4, 5}
      sb := sa[1:4]                    //[2,3,4],len:3,cap:4
      sc := sb[1:4]                    //[3,4,5],len:3,cap:3
      
          需要注意的是,新的切片的索引不能超過原切片的容量,否則會引發編譯錯誤,而不是重新分配數組
  • 在切片上追加元素(append
        如果要在切片上添加新的元素,可以使用append函數,用法爲append(被追加元素的slice, 追加的元素...)
    s1 := make([]int, 3, 6)
    s1 = append(s1, 1, 2, 3)     //[0,0,0,1,2,3],還是返回原來的slice (沒有超過原來cap)
    s1 = append(s1, 1, 2, 3)     //[0,0,0,1,2,3,1,2,3],返回的是一個新的slice(已經超過了cap,重新分配底層數組)
    
        注意,如果追加後的長度未超過原slice的容量,則返回原始的slice,如果超過了,則重新分配空間更大的數組並拷貝原始數據
  • 拷貝slice
        可以使用copy函數將一個切片拷貝到另外一個切片中,用法copy(目標slice, 被拷貝的slice)
    sc1 := []int{1, 2, 3, 4, 5}
    sc2 := []int{6, 7, 8}
    copy(sc1, sc2)
    fmt.Println(sc1)                 //[6 7 8 4 5]
    copy(sc2,sc1)
    fmt.Println(sc2)                //[6,7,8]
    copy(sc2[0:2], sc1[3:5])       //指定具體位置
    fmt.Println(sc2)               //[4 5 8]
    

Map

    Map是Go裏面的鍵值對集合,由key-value對組成,給定key,可以快速定位到對應的value。也被稱爲字典、哈希表等

  • map中的key可以是任意能夠用==或者!=操作符比較的類型,比如stringintfloat等,不能是函數、map、切片;value可以是任意類型
  • map的聲明方式var map變量名 map[key類型]vlaue類型
    var m map[int]string               //聲明
    m = map[int]string{1:"a", 2:"b"}  //初始化
    
        特別注意,map必須要初始化才能使用,即如果用上面這種方式,必須要有初始化的語句map1 = map[keyType]ValueType{},否則將報錯。當然如果覺得太繁雜,可以使用下面的make語句來替代,更加簡潔(主要是因爲使用前需要先分配好內存空間給map,使用初始化語句或者make語句才能實現空間的分配)
  • map也屬於引用類型,聲明的時候不需要知道長度,可以動態增加,可以使用make來創建
    var m1 map[int]string = make(map[int]string)
    m2 := make(map[int]string)           //簡要寫法
    m1[1] = "ok"                         //插入 (1 : ok)的key-value
    m2[1] = "good"
    
        make創建還可以指定容量,make(map[key類型]value類型,cap)cap爲容量,可以省略。超出容量會自動擴容,但爲了性能還是儘量提供一個大概的初始值。
  • 獲取指定key的value a:=m[1] (獲取key爲1的value)
  • 插入一個k-v對(或對指定key進行修改)m[1]="ok"
  • 判斷鍵值對是否存在
        上面獲取指定key的值map[key]還有另外一種用法:value,isPresent = map[key],即返回兩個值,第二個值isPresent是布爾類型,如果該key存在,該值爲true,且value爲該key對應的值;如果key不存在,則isPresentfalse,且value爲空值。
    if value, ok := map1[key1];ok{
    	fmt.Println(value)
    }
    
  • 刪除指定key的鍵值對delete(map1, key1)
  • 使用for-range對map進行遍歷
    map1 := make(map[int]string)
    map1[1] = "a"
    map1[2] = "b"
    map1[3] = "c"
    for key, value := range map1 {
    	fmt.Println(key,value)
    }
    
        如果指向獲取key或者value,可以這麼使用
    for key := range map1 {
    	fmt.Println(key)
    }
    for _, vlaue := range map1 {
    	fmt.Println(value)
    }
    
        需要注意的是,for中獲得的key和value值都是副本,直接對這兩個值進行修改並不會對原來的map有影響。需要用map[key]才能真正改變map中的值
  • map類型的切片:注意需要使用兩次make,第一次分配切片,第二次分配切片中每個map元素
    sm := make([]map[int]string, 5)
    for i := range sm {
    	sm[i] = make(map[int]string,1)
    	sm[i][1]="ok"
    }
    
  • 嵌套map的使用:即map中value也是map類型,這種情況同樣需要注意每一個嵌套的map也需要進行make之後才能使用(初始化分配空間)
    var m2 map[int]map[int]string
    m2 = make(map[int]map[int]string)
    m2[1] = make(map[int]string)     //value中嵌套的map也需要進行初始化
    m2[1][1] = "ok1"
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章