天天看點

Go學習筆記(9)Go容器類型——數組&切片&map寫在前面數組切片Map

文章目錄

  • 寫在前面
  • 數組
  • 切片
  • Map

寫在前面

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

數組

    數組是具有相同類型且長度固定的一組元素集合,定義的格式:

var 數組名 [數組長度]數組元素類型

,下面聲明一個長度為5的int型數組

arr

  • 數組元素可以是任意的原始類型,例如整型、字元串等,一個數組内所有元素的類型必須是相同的
  • 數組的長度必須是一個常量表達式,且是一個非負整數,特别注意數組的長度也是數組類型的一部分,是以不同長度的數組是不同的類型。數組的長度在聲明時就要給出。擷取數組長度用内置函數

    len(arr)

  • 當聲明數組時所有的元素都會被初始化為預設的類型零值,我們也可以在聲明數組的同時進行初始化
    var arr = [5]int{1, 2, 3, 4, 5}
    a := [3]int{1, 2, 3}
               
    還可以在初始化時僅初始化指定元素
  • 如果聲明數組時不想直接寫長度,可以用

    ...

    代替,編譯器會自動生成滿足最低長度要求的數組
  • 通過

    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)是對某個數組的一段連續片段的引用(該數組我們稱為相關數組,該片段可以是整個數組,也可以是數組中的某一段),是以切片是一個引用類型。切片底層實作是數組,它與數組的關系如下圖:

Go學習筆記(9)Go容器類型——數組&amp;切片&amp;map寫在前面數組切片Map

    多個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可以是任意能夠用

    ==

    或者

    !=

    操作符比較的類型,比如

    string

    int

    float

    等,不能是函數、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不存在,則

    isPresent

    false

    ,且

    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"
               

繼續閱讀