go語言之---數組(array)和切片(slice)


一、數組

1.什么是數組?

  1.數組是一系列同一類型數據的集合

  2.數組中包含的每個數據被稱為數組元素

  3.一個數組中包含的元素個數成為數組長度

  4.數組的長度是固定的

  5.一個數組可以由零個或者多個元素組成    

 

2.數組的申明

var arr [10]int           //10個元素的整型數組

var ptrs [5]*float64  //5個元素的指針數組,每個指針都指向float64類型 

var points [8]struct{ x, y int }  //8個元素的結構體類型

var arry [2][3]int               //2*3的二維整型數組 

a := [3]int{1, 2, 3} // 長度為3的數組

b := [5]int{1, 2, 3} //長度為10,前三個元素為1、2、3,其它默認為0

c := [...]int{4, 5, 6} //長度3的方式,Go自動計算長度

r := [...]int{9: 6}    //長度為10,最后一個元素的值為6,其它默認為0

arr2 := [2][4]int{{1, 2, 3, 4}, {5, 6, 7, 8}}//二維數組
數組的申明

  在Go語言中,數組長度在定義后就不可更改,在聲明時長度可以為一個常量或者一個常量表達式。

  

3.數組的初始化

//申明數組
var a [5]byte //長度為5的數組,每個元素為一個字節
var d [2][3]int //二維數組

//初始化數組
a = {'1','2','3'}
d = {{1,2,3},{4,5,6}}
先申明再初始化
a := [3]byte{'1', '2', '3'} //聲明並初始化一個長度為3的byte數組
a := [...]byte{'1', '2', '3'} //可以省略長度而采用`...`的方式,Go會自動根據元素個數來計算長度
d := [2][3]int{[3]int{1,2,3},[3]int{4,5,6}}
d := [2][3]int{{1,2,3},{4,5,6}} //如果內部的元素和外部的一樣,那么上面的聲明可以簡化,直接忽略內部的
類型
直接申明並初始化

 

4.數組元素訪問

  1.可以使用數組下標來訪問數組中的元素

  2.數組下標從0開始

  3.len(arr)-1則表示最后一個元素的下標

package main

import (
    "fmt"

)

func main() {
    var result int
    arr := [...]int{1, 2, 3, 4, 5}

    len := len(arr)   //len獲取數組長度

    fmt.Println("修改前:", arr)

    arr[0] = 100      //下標訪問數組元素

    result = arr[3]    //取出下標為3的元素並賦值

    fmt.Println("修改后:", arr)

    fmt.Println("數組長度:", len)

    fmt.Println("方位下標為三的元素:",result)
}


//運行結果
//修改前: [1 2 3 4 5]
//修改后: [100 2 3 4 5]
//數組長度: 5
//方位下標為三的元素: 4
元素訪問
package main

import(
"fmt"
)

func main(){
    arr := [...]int {9: 1}
    fmt.Println(arr)
    fmt.Println(len(arr))
}

//運行結果
//[0 0 0 0 0 0 0 0 0 1]
//10
計算數組長度
package main

import(
"fmt"
)

func main(){
    arr := [5]int {1, 2, 3, 4, 5}
    for i := 0; i < len(arr); i++{
        fmt.Printf("arr[%d]=%d\n", i, arr[i])
    }
}


//運行結果
//arr[0]=1
//arr[1]=2
//arr[2]=3
//arr[3]=4
//arr[4]=5
普通訪問方式,for
package main

import(
"fmt"
)

func main(){
    arr := [5]int {1, 2, 3, 4, 5}
    for i, v := range(arr) {
        fmt.Printf("arr[%d]=%d\n", i, v)
    }
}


//運行結果
//arr[0]=1
//arr[1]=2
//arr[2]=3
//arr[3]=4
//arr[4]=5
通過range訪問

 

5.數組的傳遞

  1.數組作為函數的參數仍然是值傳遞(值傳遞是復制數組給函數,傳遞后數組跟原數組沒有關系)

  2.雖然可以使用數組的指針來代替,但是改變不了數組長度。(可以改變數組內的值,但是不能改變數組的長度)

package main

import "fmt"

func main() {
    arr1 := [5]int{1, 2, 3, 4, 5}
    fmt.Println("交換前arry1= " ,arr1)

    swap(arr1 )
    
    fmt.Println("交換后arry1= " ,arr1)

    }
func swap(a [5]int)  {
    arr3 := a
    fmt.Println("值傳遞交換前arr3= ",arr3)

    c := arr3[0]
    arr3[0] = arr3[4]
    arr3[4] = c
    fmt.Println("值傳遞交換后arr3= ",arr3)

}

//運行結果
//交換前arry1=  [1 2 3 4 5]
//值傳遞交換前arr3=  [1 2 3 4 5]
//值傳遞交換后arr3=  [5 2 3 4 1]
//交換后arry1=  [1 2 3 4 5]
數組的值傳遞
package main

import "fmt"

func main() {
    arr1 := [5]int{1, 2, 3, 4, 5}
    fmt.Println("交換前arry1= " ,arr1)

    swap_pointer(&arr1 )

    fmt.Println("交換后arry1= " ,arr1)

    }

func swap_pointer(a *[5]int)  {
    var arr3 *[5]int
    arr3 = a
    fmt.Println("指針傳遞交換前arr3= ",arr3)

    c := arr3[0]
    arr3[0] = arr3[4]
    arr3[4] = c
    fmt.Println("指針傳遞交換后arr3= ",arr3)
}

//運行結果
//交換前arry1=  [1 2 3 4 5]
//指針傳遞交換前arr3=  &[1 2 3 4 5]
//指針傳遞交換后arr3=  &[5 2 3 4 1]
//交換后arry1=  [5 2 3 4 1]
數組的指針傳遞

 

6.數組的比較

  1.如果數組元素的類型是可比較的,那么這個數組也是可的比較

  2.只有數組的所有元素都相等數組才是相等的。

  3.由於長度也是數組類型的一部分,所以長度不同的數組是不等的。

  4.數組可遍歷、可修改,是否可比較,由數組元素決定。

  5.%T用於顯示一個值對應的數據類型。

 

7.數組的局限性

  1.數組的長度在定義之后無法修改。

  2.數組是值類型,每次傳遞都將產生一份副本。

  3.顯然這無法滿足開發者的某些需求。

二、切片(slice)

1.什么是切片?

  1.切片(Slice)是一個擁有相同類型元素的可變長度的序列。

  2.Go語言切片的內部結構包含地址、大小和容量。

  3.切片一般用於快速地操作一塊數據集合。

  4.slice 總是指向底層的一個 array。

  5.slice本身不是數組,slice 是一個指向 array的指針。

              切片結構和內存分布示意圖

 

2. 從數組或者一個切片中生成一個切片        

   slice [開始位置:結束位置:容量]

    a. slice 表示目標切片對象

    b. 開始位置對應目標切片對象的索引

    c. 結束位置對應目標切片的結束索引

package main

import "fmt"

func main() {

    var a = [5]int{1,2,3}
    var c []int
    c = a[1:4:5]

    fmt.Println("c的長度為:%d",len(c))
    fmt.Println("c的容量為:%d",cap(c))
    fmt.Printf("c的類型:%T",c)
}


//運行結果
//c的長度為:%d 3
//c的容量為:%d 4
//c的類型:[]int
從數組或切片中生成切片

   從數組或切片生成新的切片擁有如下特性

    a.取出的元素數量為:結束位置-開始位置

    b.取出元素不包含結束位置對應的索引,切片最后一個元素使用 slice[len(slice)] 獲取

    c.當缺省開始位置時,表示從連續區域開頭到結束位置

    d.當缺省結束位置時,表示從開始位置到整個連續區域末尾

    e.兩者同時缺省時,與切片本身等效

    f.兩者同時為0時,等效於空切片,一般用於切片復位

    (ps:根據索引位置取切片 slice 元素值時,取值范圍是(0~len(slice)-1),超界會報運行時錯誤。生成切片時,結束位置可以填寫 len(slice) 但不會報錯。)

3.直接申明新切片

  每一種類型都可以擁有其切片類型,表示多個類型元素的連續集合。

var name []T

//name 表示切片類型的變量名。
//T 表示切片類型對應的元素類型。
package main

import "fmt"

func main() {

    // 聲明字符串切片
    var strList []string
    // 聲明整型切片
    strList = []string{"asa","esd"}
    var numList []int
    // 聲明一個空切片
    numListEmpty := []int{1,2,3}
    // 輸出3個切片
    fmt.Println(strList, numList, numListEmpty)
    // 輸出3個切片大小
    fmt.Println(len(strList), len(numList), len(numListEmpty))
    // 切片判定空的結果
    fmt.Println(strList == nil)
    fmt.Println(numList == nil)
    fmt.Println(numListEmpty == nil)

}

//運行結果
//[asa esd] [] [1 2 3]
//2 0 3
//false
//true
//false
直接聲明新的切片

  ps:  1. 切片是動態結構,只能與nil判定相等,不能互相判等。

    2. 聲明新的切片后,可以使用append() 函數來添加元素。    

 

4.使用 make() 函數構造切片

  語法:

    make( []T, size, cap )

      T:切片的元素類型

      size:就是為這個類型分配多少個元素

      cap:預分配的元素數量,這個值設定后不影響 size,只是能提前分配空間,降低多次分配空間造成的性能問題

package main

import "fmt"

func main() {

    a := make([]int, 2)
    b := make([]int, 2, 10)
    fmt.Println(a, b)
    fmt.Println(len(a), len(b))


}


//運行結果
//[0 0] [0 0]
//2 2
使用make函數創建切片

  1. a 和 b 均是預分配 2 個元素的切片,只是 b 的內部存儲空間已經分配了 10 個,但實際使用了 2 個元素。

  2. 容量不會影響當前的元素個數,因此 a 和 b 取 len 都是 2。  

 

(ps:使用 make() 函數生成的切片一定發生了內存分配操作。但給定開始與結束位置(包括切片復位)的切片只是將新的切片結構指向已經分配好的內存區域,設定開始與結束位置,不會發生內存分配操作。)

 

5、使用append向切片追加單個元素

  1.append() 可以為切片動態添加元素。

  2.每個切片會指向一片內存空間,這片空間能容納一定數量的元素。

  3.當空間不能容納足夠多的元素時,切片就會進行“擴容”。

  4.“擴容”操作往往發生在 append() 函數調用時。

package main

import "fmt"

func main() {
        //聲明一個整型切片。
    var numbers []int
       
    for i := 0; i < 10; i++ {
                //循環向 numbers 切片添加10個數。
        numbers = append(numbers, i)
                //打印輸出切片的長度、容量和指針變化。使用 len() 函數查看切片擁有的元素個數,使用 cap() 函數查看切片的容量情況
        fmt.Printf("len: %d  cap: %d pointer: %p\n", len(numbers), cap(numbers), numbers)
    }
}

//運行結果
//len: 1  cap: 1 pointer: 0xc00001c060
//len: 2  cap: 2 pointer: 0xc00001c090
//len: 3  cap: 4 pointer: 0xc000016120
//len: 4  cap: 4 pointer: 0xc000016120
//len: 5  cap: 8 pointer: 0xc00001e180
//len: 6  cap: 8 pointer: 0xc00001e180
//len: 7  cap: 8 pointer: 0xc00001e180
//len: 8  cap: 8 pointer: 0xc00001e180
//len: 9  cap: 16 pointer: 0xc000088000
//len: 10  cap: 16 pointer: 0xc000088000                                
append時,切片擴容分析

  a.len() 函數並不等於 cap。

  b.當元素個數超過cap()的數量時,切片會進行擴容

  c.擴容后切片的內存地址發生改變

  d.但是切片的名稱沒有發生改變。

 

6、使用append向切片中追加多個元素/其他切片

package main

import "fmt"

func main() {

    var car []string

    // 添加1個元素
    car = append(car, "OldDriver")

    // 添加多個元素
    car = append(car, "Ice", "Sniper", "Monk")
    // 添加切片
    team := []string{"Pig", "Flyingcake", "Chicken"}
    car = append(car, team...)
    fmt.Println(car)
    
}

//運行結果
//[OldDriver Ice Sniper Monk Pig Flyingcake Chicken]
append追加多個元素

  在team后面加上了...,表示將 team 整個添加到 car 的后面。

  

 

 

 

 

 

 

 

   


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM