zoukankan      html  css  js  c++  java
  • 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
    ", 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
    ", 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
    ", 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 的后面。

      

       

  • 相关阅读:
    Python Revisited Day 13 (正则表达式)
    Python Revisited Day 06 (面向对象程序设计)
    Python Revisited (变量)
    Python Revisited Day 05(模块)
    Python Revisited Day 04 (控制结构与函数)
    Python Revisited Day 03 (组合数据类型)
    Numpy
    Python Revisited Day 01
    Python3使用openpyxl读写Excel文件
    Python3操作YAML文件
  • 原文地址:https://www.cnblogs.com/ppzhang/p/10558561.html
Copyright © 2011-2022 走看看