美文网首页
第4章-数组和切片

第4章-数组和切片

作者: Venture_Mark | 来源:发表于2018-10-15 16:27 被阅读0次

    一、数组(Array)

    1.1 什么是数组

    Go 语言提供了数组类型的数据结构。 数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,这种类型可以是任意的原始类型例如整形、字符串或者自定义类型。

    数组元素可以通过索引(位置)来读取(或者修改),索引从0开始,第一个元素索引为 0,第二个索引为 1,以此类推。数组的下标取值范围是从0开始,到长度减1。

    数组一旦定义后,大小不能更改。

    1.2 数组的语法

    声明和初始化数组

    var variable_name [SIZE] variable_type
    
    
    var balance [10] float32
    
    
    var balance = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
    
    

    初始化数组中 {} 中的元素个数不能大于 [] 中的数字。 如果忽略 [] 中的数字不设置数组大小,Go 语言会根据元素的个数来设置数组的大小:

    var balance = []float32{1000.0, 2.0, 3.4, 7.0, 50.0}
    
    
    balance[4] = 50.0
    
    

    访问数组元素

    float32 salary = balance[9]
    
    

    示例代码:

    package main
    
    import "fmt"
    
    func main() {
       var n [10]int /* n 是一个长度为 10 的数组 */
       var i,j int
    
       /* 为数组 n 初始化元素 */
       for i = 0; i < 10; i++ {
          n[i] = i + 100 /* 设置元素为 i + 100 */
       }
    
       /* 输出每个数组元素的值 */
       for j = 0; j < 10; j++ {
          fmt.Printf("Element[%d] = %d\n", j, n[j] )
       }
    }
    
    

    结果

    Element[0] = 100
    Element[1] = 101
    Element[2] = 102
    Element[3] = 103
    Element[4] = 104
    Element[5] = 105
    Element[6] = 106
    Element[7] = 107
    Element[8] = 108
    Element[9] = 109
    
    

    数组的长度

    通过将数组作为参数传递给len函数,可以获得数组的长度。

    示例代码:

    package main
    
    import "fmt"
    
    func main() {
        a := [...]float64{67.7, 89.8, 21, 78}
        fmt.Println("length of a is",len(a))
    
    }
    
    

    运行结果:

    length of a is 4
    
    

    您甚至可以忽略声明中数组的长度并将其替换为…让编译器为你找到长度。这是在下面的程序中完成的。

    示例代码:

    package main
    
    import (
        "fmt"
    )
    
    func main() {
        a := [...]int{12, 78, 50} // ... makes the compiler determine the length
        fmt.Println(a)
    }
    
    

    遍历数组:

    package main
    
    import "fmt"
    
    func main() {
        a := [...]float64{67.7, 89.8, 21, 78}
        for i := 0; i < len(a); i++ { //looping from 0 to the length of the array
            fmt.Printf("%d th element of a is %.2f\n", i, a[i])
        }
    }
    
    

    使用range遍历数组:

    package main
    
    import "fmt"
    
    func main() {
        a := [...]float64{67.7, 89.8, 21, 78}
        sum := float64(0)
        for i, v := range a {//range returns both the index and value
            fmt.Printf("%d the element of a is %.2f\n", i, v)
            sum += v
        }
        fmt.Println("\nsum of all elements of a",sum)
    }
    
    

    如果您只需要值并希望忽略索引,那么可以通过使用_ blank标识符替换索引来实现这一点。

    for _, v := range a { //ignores index
    }
    
    

    1.3 多维数组

    var threedim [5][10][4]int
    
    

    三维数组

    a = [3][4]int{
     {0, 1, 2, 3} ,   /*  第一行索引为 0 */
     {4, 5, 6, 7} ,   /*  第二行索引为 1 */
     {8, 9, 10, 11}   /*  第三行索引为 2 */
    }
    
    
    int val = a[2][3]
    
    

    1.4 数组是值类型

    数组是值类型 Go中的数组是值类型,而不是引用类型。这意味着当它们被分配给一个新变量时,将把原始数组的副本分配给新变量。如果对新变量进行了更改,则不会在原始数组中反映。

    package main
    
    import "fmt"
    
    func main() {
        a := [...]string{"USA", "China", "India", "Germany", "France"}
        b := a // a copy of a is assigned to b
        b[0] = "Singapore"
        fmt.Println("a is ", a)
        fmt.Println("b is ", b)
    }
    
    

    运行结果:

    a is [USA China India Germany France]
    b is [Singapore China India Germany France]
    
    

    类似地,当将数组传递给函数作为参数时,它们将通过值传递,而原始数组将保持不变。

    向函数传递数组

    第一种

    void myFunction(param [10]int)
    {
    .
    .
    .
    }
    
    

    第二种

    void myFunction(param []int)
    {
    .
    .
    .
    }
    
    
    func getAverage(arr []int, int size) float32
    {
       var i int
       var avg, sum float32
    
       for i = 0; i < size; ++i {
          sum += arr[i]
       }
    
       avg = sum / size
    
       return avg;
    }
    
    
    package main
    
    import "fmt"
    
    func changeLocal(num [5]int) {
        num[0] = 55
        fmt.Println("inside function ", num)
    
    }
    func main() {
        num := [...]int{5, 6, 7, 8, 8}
        fmt.Println("before passing to function ", num)
        changeLocal(num) //num is passed by value
        fmt.Println("after passing to function ", num)
    }
    
    

    运行结果:

    before passing to function  [5 6 7 8 8]
    inside function  [55 6 7 8 8]
    after passing to function  [5 6 7 8 8]
    
    

    数组的大小是类型的一部分。因此[5]int和[25]int是不同的类型。因此,数组不能被调整大小。不要担心这个限制,因为切片的存在是为了解决这个问题。

    package main
    
    func main() {
        a := [3]int{5, 78, 8}
        var b [5]int
        b = a //not possible since [3]int and [5]int are distinct types
    }
    
    

    二、切片(Slice)

    2.1 什么是切片

    Go 语言切片是对数组的抽象。 Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型切片(“动态数组”),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大

    切片是一种方便、灵活且强大的包装器。切片本身没有任何数据。它们只是对现有数组的引用。

    切片与数组相比,不需要设定长度,在[]中不用设定值,相对来说比较自由

    从概念上面来说slice像一个结构体,这个结构体包含了三个元素:

    1. 指针,指向数组中slice指定的开始位置
    2. 长度,即slice的长度
    3. 最大长度,也就是slice开始位置到数组的最后位置的长度

    2.2 切片的语法

    定义切片

    var identifier []type
    
    

    切片不需要说明长度。 或使用make()函数来创建切片:

    var slice1 []type = make([]type, len)
    也可以简写为
    slice1 := make([]type, len)
    
    
    make([]T, length, capacity)
    
    

    初始化

    s[0] = 1
    s[1] = 2
    s[2] = 3
    
    
    s :=[] int {1,2,3 }
    
    
    s := arr[startIndex:endIndex]
    
    

    将arr中从下标startIndex到endIndex-1 下的元素创建为一个新的切片(前闭后开),长度为endIndex-startIndex

    s := arr[startIndex:]
    
    

    缺省endIndex时将表示一直到arr的最后一个元素

    s := arr[:endIndex]
    
    

    缺省startIndex时将表示从arr的第一个元素开始

    package main
    
    import (
        "fmt"
    )
    
    func main() {
        a := [5]int{76, 77, 78, 79, 80}
        var b []int = a[1:4] //creates a slice from a[1] to a[3]
        fmt.Println(b)
    }
    
    

    2.3 修改切片

    slice没有自己的任何数据。它只是底层数组的一个表示。对slice所做的任何修改都将反映在底层数组中。

    示例代码:

    package main
    
    import (
        "fmt"
    )
    
    func main() {
        darr := [...]int{57, 89, 90, 82, 100, 78, 67, 69, 59}
        dslice := darr[2:5]
        fmt.Println("array before",darr)
        for i := range dslice {
            dslice[i]++
        }
        fmt.Println("array after",darr)
    }
    
    

    运行结果:

    array before [57 89 90 82 100 78 67 69 59]
    array after [57 89 91 83 101 78 67 69 59]
    
    

    当多个片共享相同的底层数组时,每个元素所做的更改将在数组中反映出来。

    示例代码:

    package main
    
    import (
        "fmt"
    )
    
    func main() {
        numa := [3]int{78, 79 ,80}
        nums1 := numa[:] //creates a slice which contains all elements of the array
        nums2 := numa[:]
        fmt.Println("array before change 1",numa)
        nums1[0] = 100
        fmt.Println("array after modification to slice nums1", numa)
        nums2[1] = 101
        fmt.Println("array after modification to slice nums2", numa)
    }
    
    

    运行结果:

    array before change 1 [78 79 80]
    array after modification to slice nums1 [100 79 80]
    array after modification to slice nums2 [100 101 80]
    
    

    2.4 len() 和 cap() 函数

    切片的长度是切片中元素的数量。切片的容量是从创建切片的索引开始的底层数组中元素的数量。

    切片是可索引的,并且可以由 len() 方法获取长度 切片提供了计算容量的方法 cap() 可以测量切片最长可以达到多少

    package main
    
    import "fmt"
    
    func main() {
       var numbers = make([]int,3,5)
    
       printSlice(numbers)
    }
    
    func printSlice(x []int){
       fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
    }
    
    

    结果

    len=3 cap=5 slice=[0 0 0]
    
    

    空切片

    一个切片在未初始化之前默认为 nil,长度为 0

    package main
    
    import "fmt"
    
    func main() {
       var numbers []int
    
       printSlice(numbers)
    
       if(numbers == nil){
          fmt.Printf("切片是空的")
       }
    }
    
    func printSlice(x []int){
       fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
    }
    
    

    结果

    len=0 cap=0 slice=[]
    切片是空的
    
    
    package main
    
    import "fmt"
    
    func main() {
       /* 创建切片 */
       numbers := []int{0,1,2,3,4,5,6,7,8}
       printSlice(numbers)
    
       /* 打印原始切片 */
       fmt.Println("numbers ==", numbers)
    
       /* 打印子切片从索引1(包含) 到索引4(不包含)*/
       fmt.Println("numbers[1:4] ==", numbers[1:4])
    
       /* 默认下限为 0*/
       fmt.Println("numbers[:3] ==", numbers[:3])
    
       /* 默认上限为 len(s)*/
       fmt.Println("numbers[4:] ==", numbers[4:])
    
       numbers1 := make([]int,0,5)
       printSlice(numbers1)
    
       /* 打印子切片从索引  0(包含) 到索引 2(不包含) */
       number2 := numbers[:2]
       printSlice(number2)
    
       /* 打印子切片从索引 2(包含) 到索引 5(不包含) */
       number3 := numbers[2:5]
       printSlice(number3)
    
    }
    
    func printSlice(x []int){
       fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
    }
    
    

    结果

    len=9 cap=9 slice=[0 1 2 3 4 5 6 7 8]
    numbers == [0 1 2 3 4 5 6 7 8]
    numbers[1:4] == [1 2 3]
    numbers[:3] == [0 1 2]
    numbers[4:] == [4 5 6 7 8]
    len=0 cap=5 slice=[]
    len=2 cap=9 slice=[0 1]
    len=3 cap=7 slice=[2 3 4]
    
    

    2.5 append() 和 copy() 函数

    append 向slice里面追加一个或者多个元素,然后返回一个和slice一样类型的slice copy 函数copy从源slice的src中复制元素到目标dst,并且返回复制的元素的个数

    append函数会改变slice所引用的数组的内容,从而影响到引用同一数组的其它slice。 但当slice中没有剩 余空间(即(cap-len) == 0)时,此时将动态分配新的数组空间。返回的slice数组指针将指向这个空间,而原 数组的内容将保持不变;其它引用此数组的slice则不受影响

    下面的代码描述了从拷贝切片的 copy 方法和向切片追加新元素的 append 方法

    package main
    
    import "fmt"
    
    func main() {
       var numbers []int
       printSlice(numbers)
    
       /* 允许追加空切片 */
       numbers = append(numbers, 0)
       printSlice(numbers)
    
       /* 向切片添加一个元素 */
       numbers = append(numbers, 1)
       printSlice(numbers)
    
       /* 同时添加多个元素 */
       numbers = append(numbers, 2,3,4)
       printSlice(numbers)
    
       /* 创建切片 numbers1 是之前切片的两倍容量*/
       numbers1 := make([]int, len(numbers), (cap(numbers))*2)
    
       /* 拷贝 numbers 的内容到 numbers1 */
       copy(numbers1,numbers)
       printSlice(numbers1)
    }
    
    func printSlice(x []int){
       fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
    }
    
    

    结果

    len=0 cap=0 slice=[]
    len=1 cap=2 slice=[0]
    len=2 cap=2 slice=[0 1]
    len=5 cap=8 slice=[0 1 2 3 4]
    len=5 cap=12 slice=[0 1 2 3 4]
    
    

    numbers1与numbers两者不存在联系,numbers发生变化时,numbers1是不会随着变化的。也就是说copy方法是不会建立两个切片的联系的

    相关文章

      网友评论

          本文标题:第4章-数组和切片

          本文链接:https://www.haomeiwen.com/subject/xpnfzftx.html