11-Go語言數組和切片-指趣學院

數組

  • 和C語言一樣,Go語言中也有數組的概念, Go語言中的數組也是用于保存一組相同類型的數據
  • 和C語言一樣,Go語言中的數組也分為一維數組多維數組

一維數組

  • 格式:var arr [元素個數]數據類型
    • 和C語言中數組不同, Go語言中數組定義之后就有默認的初始值
    • 默認初始值就是保存數據類型的默認值(零值)
    package main
    import "fmt"
    func main() {
        // 1.定義一個數組
        var arr [3]int
        // 2.打印數組
        fmt.Println(arr) //[0 0 0]
    
        // 1.定義一個數組
        var arr [3]bool
        // 2.打印數組
        fmt.Println(arr) //[false false  false]
    }
    
    • 和C語言一樣,Go語言中的數組也提供了好幾種初始化方式
    package main
    import "fmt"
    func main() {
          // 1.定義的同時完全初始化
          var arr1 [3]int = [3]int{1, 3, 5}
          // 2.打印數組
          fmt.Println(arr1) // [1 3 5]
    
          // 1.定義的同時部分初始化
          var arr4 [3]int = [3]int{8, 9}
          // 2.打印數組
          fmt.Println(arr4) // [8 9 0]
    
          // 1.定義的同時指定元素初始化
          var arr5 [3]int = [3]int{0:8, 2:9}
          // 2.打印數組
          fmt.Println(arr5) // [8 0 9]
    
          // 1.先定義再逐個初始化
          var arr3 [3]int
          arr3[0] = 1
          arr3[1] = 2
          arr3[2] = 3
          // 2.打印數組
          fmt.Println(arr3) // [1 2 3]
    }
    
    • 和C語言中的數組不同,Go語言中數組除了可以定義的同時初始化以外,還可以先定義再一次性初始化
    package main
    import "fmt"
    func main() {
          // 1.先定義再一次性初始化
          var arr2 [3]int
          arr2 = [3]int{2, 4, 6}
          // 2.打印數組
          fmt.Println(arr2) // [2 4 6]
    }
    
    • 和C語言一樣,Go語言中如果定義數組的同時初始化,那么元素個數可以省略,但是必須使用...來替代
      • ...會根據初始化元素個數自動確定數組長度
    package main
    import "fmt"
    func main() {
      // 1.定義的同時完全初始化
      var arr1  = [...]int{1, 3, 5}
      // 2.打印數組
      fmt.Println(arr1) // [1 3 5]
    
      // 1.定義的同時指定元素初始化
      var arr2  = [...]int{6:5}
      // 2.打印數組
      fmt.Println(arr2) // [0 0 0 0 0 0 5]
    }
    
  • Go語言中數組的訪問和使用方式和C語言一樣都是通過數組名稱[索引]的方式
package main
import "fmt"
func main() {
    arr := [...]int{1, 3, 5}
    // 使用數組, 往數組中存放數據
    arr[1] = 666
    // 訪問數組, 從數組中獲取數據
    fmt.Println(arr[0])
    fmt.Println(arr[1])
    fmt.Println(arr[2])
}
  • 遍歷數組
    • Go語言中提供了兩種遍歷數組的方式, 一種是通過傳統for循環遍歷, 一種是通過for...range循環遍歷
package main
import "fmt"
func main() {
    arr := [...]int{1, 3, 5}
    // 傳統for循環遍歷
    for i:=0; i<len(arr); i++{
        fmt.Println(i, arr[i])
    }
    // for...range循環遍歷
    for i, v := range arr{
        fmt.Println(i, v)
    }
}
  • 數組注意點
    • Go語言中數組長度也是數據類型的一部分
    package main
    import "fmt"
    func main() {
        var arr1 [3]int
        var arr2 [3]int
        //var arr3 [2]int
        fmt.Println(arr1 == arr2) // true
        //fmt.Println(arr1 == arr3) // 編譯報錯, 不是相同類型不能比較
    }
    
    • 如果元素類型支持==、!=操作時,那么數組也支持此操作
    package main
    import "fmt"
    func main() {
        var arr1 [3]int = [3]int{1, 3, 5}
        var arr2 [3]int = [3]int{1, 3, 5}
        var arr3 [3]int = [3]int{2, 4, 6}
        // 首先會判斷`數據類型`是否相同,如果相同會依次取出數組中`對應索引的元素`進行比較, 
        // 如果所有元素都相同返回true,否則返回false
        fmt.Println(arr1 == arr2) // true
        fmt.Println(arr1 == arr3) // false
     }
    
    • Go語言中的數組是值類型, 賦值和傳參時會復制整個數組
    package main
    import "fmt"
    func main() {
      var arr1 [3]int = [3]int{1, 3, 5}
      var arr2 [3]int = arr1
      arr2[0] = 666
      fmt.Println(arr1) // [1 3 5]
      fmt.Println(arr2) // [666 3 5]
     }
    

二維數組

  • 用法和C語言數組一樣, 只是創建的格式不同
  • 格式: [行數][列數]類型
  package main
  import "fmt"
  func main() {
    // 創建一個兩行三列數組
    arr := [2][3]int{
        {1, 2, 3},
        {4, 5, 6}, //注意: 數組換行需要以逗號結尾
    }
    fmt.Println(arr)// [[1 2 3] [4 5 6]]
   }
  • 創建多維數組時只允許第一維度使用...
  • 格式: [...][列數]類型
package main
  import "fmt"
  func main() {
    // 創建一個兩行三列數組
    arr := [...][3]int{
        {1, 2, 3},
        {4, 5, 6},
    }
    fmt.Println(arr)// [[1 2 3] [4 5 6]]
   }

切片

  • 無論是C語言中的數組還是Go語言中的數組,數組的長度一旦確定就不能改變, 但在實際開發中我們可能事先不能確定數組的長度, 為了解決這類問題Go語言中推出了一種新的數據類型切片
  • 切片可以簡單的理解為長度可以變化的數組, 但是Go語言中的切片本質上是一個結構體
    • 切片源碼
    type slice struct{
      array unsafe.Pointer // 指向底層數組指針
      len int // 切片長度(保存了多少個元素)
      cap int // 切片容量(可以保存多少個元素)
    }
    

  • 切片創建的三種方式
  • 方式一: 通過數組創建切片array[startIndex:endIndex]
    package main
    import "fmt"
    func main() {
        var arr = [5]int{1, 3, 5, 7, 9}
        // 從數組0下標開始取,一直取到2下標前面一個索引
        var sce = arr[0:2]
        fmt.Println(sce) // [1 3]
        // 切片len = 結束位置 - 開始位置
        fmt.Println(len(sce)) // 2 - 0 = 2
        fmt.Println(cap(sce)) // 5 - 0 = 5
        // 數組地址就是數組首元素的地址
        fmt.Printf("%p\n", &arr) // 0xc04200a330
        fmt.Printf("%p\n", &arr[0]) // 0xc04200a330
        // 切片地址就是數組中指定的開始元素的地址
        //  arr[0:2]開始地址為0, 所以就是arr[0]的地址
        fmt.Printf("%p\n", sce) // 0xc04200a330
    }
    
  • image.png
    package main
    import "fmt"
    func main() {
      var arr = [5]int{1, 3, 5, 7, 9}
      // 根據數組的索引片段創建切片
      var sce = arr[2:4]
      fmt.Println(sce) // [5 7]
      fmt.Println(len(sce)) // 4 - 2 = 2
      fmt.Println(cap(sce)) // 5 - 2 = 3
      fmt.Printf("%p\n", &arr[2]) // 0xc042076070
      fmt.Printf("%p\n", sce) // 0xc042076070
    }
    
  • 指定起始位置時有三種方式可以指定
    • 開始位置和結束位置都指定
    • 只指定開始位置或結束位置
    • 開始位置和結束位置都不指定
  package main
  import "fmt"
  func main() {
    var arr = [5]int{1, 3, 5, 7, 9}
    // 同時指定開始位置和結束位置
    var sce1 = arr[0:2]
    fmt.Println(sce1) // [1 3]

    // 只指定結束位置
    var sce3 = arr[:2]
    fmt.Println(sce3) // [1 3]

    // 只指定開始位置
    var sce2 = arr[0:]
    fmt.Println(sce2) // [1 3 5 7 9]

    // 都不指定
    var sce4 = arr[:]
    fmt.Println(sce4) // [1 3 5 7 9]
  }
  • 方式二: 通過make函數創建make(類型, 長度, 容量)
    • 內部會先創建一個數組, 然后讓切片指向數組
    • 如果沒有指定容量,那么容量和長度一樣
 package main
 import "fmt"
 func main() {
    // 第一個參數: 指定切片數據類型
    // 第二個參數: 指定切片的長度
    // 第三個參數: 指定切片的容量
    var sce = make([]int, 3, 5)
    fmt.Println(sce) // [0 0 0]
    fmt.Println(len(sce)) // 3
    fmt.Println(cap(sce)) // 5
    /*
    內部實現原理
    var arr = [5]int{0, 0, 0}
    var sce = arr[0:3]
    */
 }
  • 方式三:通過Go提供的語法糖快速創建
    • 和創建數組一模一樣, 但是不能指定長度
    • 通過該方式創建時, 切片的長度和容量相等
 package main
 import "fmt"
 func main() {
    var sce = []int{1, 3, 5}
    fmt.Println(sce) // [1 3 5]
    fmt.Println(len(sce)) // 3
    fmt.Println(cap(sce)) // 3
 }

  • 切片的使用
    • 切片的基本使用方式和數組一樣, 可以通過切片名稱[索引]方式操作切片
     package main
     import "fmt"
     func main() {
        var sce = []int{1, 3, 5}
        // 使用切片, 往切片中存放數據
        sce[1] = 666
        // 訪問切片, 從切片中獲取數據
        fmt.Println(sce) // [1 666 5]
     }
    
    • 和數組一樣, 如果通過切片名稱[索引]方式操作切片, 不能越界
      package main
      import "fmt"
      func main() {
         var sce = []int{1, 3, 5}
         // 編譯報錯, 越界
         sce[3] = 666
      }
    
    • 如果希望切片自動擴容,那么添加數據時必須使用append方法
      • append函數會在切片末尾添加一個元素, 并返回一個追加數據之后的切片
      • 利用append函數追加數據時,如果追加之后沒有超出切片的容量,那么返回原來的切片, 如果追加之后超出了切片的容量,那么返回一個新的切片
      • append函數每次給切片擴容都會按照原有切片容量*2的方式擴容
      package main
      import "fmt"
      func main() {
       var sce = []int{1, 3, 5}
       fmt.Println("追加數據前:", sce) // [1 3 5]
       fmt.Println("追加數據前:", len(sce)) // 3
       fmt.Println("追加數據前:", cap(sce)) // 3
       fmt.Printf("追加數據前: %p\n", sce) // 0xc0420600a0
       // 第一個參數: 需要把數據追加到哪個切片中
       // 第二個參數: 需要追加的數據, 可以是一個或多個
       sce = append(sce, 666)
       fmt.Println("追加數據后:", sce) // [1 3 5 666]
       fmt.Println("追加數據后:", len(sce)) // 4
       fmt.Println("追加數據后:", cap(sce)) // 6
       fmt.Printf("追加數據前: %p\n", sce) // 0xc042076b60
      }
    
    • 除了append函數外,Go語言還提供了一個copy函數, 用于兩個切片之間數據的快速拷貝
      • 格式: copy(目標切片, 源切片), 會將源切片中數據拷貝到目標切片中
       package main
       import "fmt"
       func main() {
      var sce1 = []int{1, 3, 5}
      var sce2 = make([]int, 5)
      fmt.Printf("賦值前:%p\n", sce1) // 0xc0420600a0
      fmt.Printf("賦值前:%p\n", sce2) // 0xc042076060
      // 將sce2的指向修改為sce1, 此時sce1和sce2底層指向同一個數組
      sce2 = sce1
      fmt.Printf("賦值后:%p\n", sce1) // 0xc0420600a0
      fmt.Printf("賦值后:%p\n", sce2) // 0xc0420600a0
      //copy(sce2, sce1)
      fmt.Println(sce1) // [1 3 5]
      fmt.Println(sce2) // [1 3 5]
      sce2[1] = 666
      fmt.Println(sce1) // [1 666 5]
      fmt.Println(sce2) // [1 666 5]
       }
    
       package main
       import "fmt"
       func main() {
      var sce1 = []int{1, 3, 5}
      var sce2 = make([]int, 5)
      fmt.Printf("賦值前:%p\n", sce1) // 0xc0420600a0
      fmt.Printf("賦值前:%p\n", sce2) // 0xc042076060
      // 將sce1中的數據拷貝到sce2中,, 此時sce1和sce2底層指向不同數組
      copy(sce2, sce1)
      fmt.Printf("賦值后:%p\n", sce1) // 0xc0420600a0
      fmt.Printf("賦值后:%p\n", sce2) // 0xc042076060
      //copy(sce2, sce1)
      fmt.Println(sce1) // [1 3 5]
      fmt.Println(sce2) // [1 3 5 0 0]
      sce2[1] = 666
      fmt.Println(sce1) // [1 3 5]
      fmt.Println(sce2) // [1 666 5 0 0]
       }
    
    • copy函數在拷貝數據時永遠以小容量為準
       package main
       import "fmt"
       func main() {
      // 容量為3
      var sce1 = []int{1, 3, 5}
      // 容量為5
      var sce2 = make([]int, 5)
      fmt.Println("拷貝前:", sce2) // [0 0 0 0 0]
      // sce2容量足夠, 會將sce1所有內容拷貝到sce2
      copy(sce2, sce1)
      fmt.Println("拷貝后:", sce2) // [1 3 5 0 0]
       }
    
       package main
       import "fmt"
       func main() {
      // 容量為3
      var sce1 = []int{1, 3, 5}
      // 容量為2
      var sce2 = make([]int, 2)
      fmt.Println("拷貝前:", sce2) // [0 0]
      // sce2容量不夠, 會將sce1前2個元素拷貝到sce2中
      copy(sce2, sce1)
      fmt.Println("拷貝后:", sce2) // [1 3]
       }
    

  • 切片的注意點
    • 可以通過切片再次生成新的切片, 兩個切片底層指向同一數組
      package main
      import "fmt"
      func main() {
          arr := [5]int{1, 3, 5, 7, 9}
          sce1 := arr[0:4]
          sce2 := sce1[0:3]
          fmt.Println(sce1) // [1 3 5 7]
          fmt.Println(sce2) // [1 3 5]
          // 由于底層指向同一數組, 所以修改sce2會影響sce1
          sce2[1] = 666
          fmt.Println(sce1) // [1 666 5 7]
          fmt.Println(sce2) // [1 666 5]
       }
    
    • 和數組不同, 切片只支持判斷是否為nil, 不支持==、!=判斷
    package main
    import "fmt"
    func main() {
        var arr1 [3]int = [3]int{1, 3, 5}
        var arr2 [3]int = [3]int{1, 3, 5}
        var arr3 [3]int = [3]int{2, 4, 6}
        // 首先會判斷`數據類型`是否相同,如果相同會依次取出數組中`對應索引的元素`進行比較, 
        // 如果所有元素都相同返回true,否則返回false
        fmt.Println(arr1 == arr2) // true
        fmt.Println(arr1 == arr3) // false
    
        sce1 := []int{1, 3, 5}
        sce2 := []int{1, 3, 5}
        //fmt.Println(sce1 == sce2) // 編譯報錯
        fmt.Println(sce1 != nil) // true
        fmt.Println(sce2 == nil) // false
     }
    
    • 只聲明當沒有被創建的切片是不能使用的
    package main
    import "fmt"
    func main() {
      // 數組聲明后就可以直接使用, 聲明時就會開辟存儲空間
      var arr [3]int
      arr[0] = 2
      arr[1] = 4
      arr[2] = 6
      fmt.Println(arr) // [2 4 6]
    
      // 切片聲明后不能直接使用, 只有通過make或語法糖創建之后才會開辟空間,才能使用
      var sce []int
      sce[0] = 2 // 編譯報錯
      sce[1] = 4
      sce[2] = 6
      fmt.Println(sce)
     }
    
    • 字符串的底層是[]byte數組, 所以字符也支持切片相關操作
    package main
    import "fmt"
    func main() {
      str := "abcdefg"
      // 通過字符串生成切片
      sce1 := str[3:]
      fmt.Println(sce1) // defg
    
      sce2 := make([]byte, 10)
      // 第二個參數只能是slice或者是數組
      // 將字符串拷貝到切片中
      copy(sce2, str)
      fmt.Println(sce2) //[97 98 99 100 101 102 103 0 0 0]
     }
    
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,501評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,673評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,610評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,939評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,668評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,004評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,001評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,173評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,705評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,426評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,656評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,139評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,833評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,247評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,580評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,371評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,621評論 2 380

推薦閱讀更多精彩內容

  • 第四天 數組【悟空教程】 第04天 Java基礎 第1章數組 1.1數組概念 軟件的基本功能是處理數據,而在處理數...
    Java幫幫閱讀 1,611評論 0 9
  • SwiftDay011.MySwiftimport UIKitprintln("Hello Swift!")var...
    smile麗語閱讀 3,854評論 0 6
  • 模式與用戶的區別:比喻為一個人(用戶)與他所擁有的所有的錢(模式),他的錢是由那些一塊的,十塊的,100塊的之類的...
    我是一個好人嗎閱讀 263評論 0 0
  • 一、我們打完了吧 還有兩天假期就要結束了,米媽媽要帶著外公外婆回家,我也要帶著大米回家了,本以為會有個快樂的結局,...
    燕山書童閱讀 362評論 0 0
  • 11點半已經到了飯點,我們都打算去吃飯了,看霍琪準備好裝備要出發的樣子,“要吃飯了,這是去哪啊”?“3#加氫剛才打...
    miss敏敏閱讀 151評論 0 0