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 的后面。

      

       

  • 相关阅读:
    TP5.1 查看具体版本
    require(): open_basedir restriction in effect. 解决方法
    解决php -v查看到版本于phpinfo()打印的版本不一致问题
    Session机制详解
    c# 获取项目的根目录
    会修电脑不会修收音机?-闲聊设计模式原则
    CSV格式数据如何导入SqlServer?
    CSV格式数据如何导入MySQL?
    反射—程序员的快乐 -08
    策略模式 是一种好策略 -07
  • 原文地址:https://www.cnblogs.com/ppzhang/p/10558561.html
Copyright © 2011-2022 走看看