zoukankan      html  css  js  c++  java
  • Golang数据类型之数组

    有过PythonJavaScript编程经验的人都知道其数组是动态的,可以随需求自动增大数组长度,而Go里面的数组长度却是固定的,无法扩大或者缩小

    Go中也有类似的动态"数组",称为切片slice

    Go中的数组是slicemap两种数据类型的基础,这两种数据类型的底层都是通过数组实现的

    1、存储方式

    当在Go中声明一个数组之后,会在内存中开辟一段固定长度的、连续的空间存放数组中的各个元素,这些元素的数据类型完全相同,可以是内置的简单数据类型(intstring等),也可以是自定义的struct`结构体类型

    • 固定长度:这意味着数组不可增长、不可缩减。想要扩展数组,只能创建新数组,将原数组的元素复制到新数组
    • 连续空间:这意味可以在缓存中保留的时间更长,搜索速度更快,是一种非常高效的数据结构,同时还意味着可以通过数值index的方式访问数组中的某个元素
    • 数据类型:意味着限制了每个block中可以存放什么样的数据,以及每个block可以存放多少字节的数据

    例如,使用下面的语句声明一个长度为4int类型的数组,那么这个数组最多只能存放4个元素,且所有元素都只能是int类型。同时,还为这个数组做了初始化

    array := [4]int{3, 1, 4,1 }
    

    2、声明与初始化

    2.1 声明语法

    Go语言数组声明需要指定元素类型及元素个数,语法格式如下

    var variable_name [SIZE] variable_type
    

    比如声明一个长度为5, 类型是float64的数组

    var arrayf [5]float64
    

    2.2 数组类型

    虽然称呼数组为int类型的数组,但数组的数据类型是两部分组成的[n]TYPE,这个整体才是数组的数据类型。所以,[4]int[5]int是两种不同的数组类型

    var (
        a1 [4]int
        a2 [5]int
    )
    fmt.Println(reflect.TypeOf(a1))
    fmt.Println(reflect.TypeOf(a2))
    

    2.3 数组默认值

    当一个变量被声明之后, 都会立即赋予一个默认值, 数组的默认值和数组的数据类型有关

    var a1 [5]int
    fmt.Println(a1) // [0 0 0 0 0]
    var a2 [4]string
    fmt.Println(a2) // [   ]
    

    2.4 声明并初始化

    如果不想填充默认值, 可以声明时就赋值

    a1 := [3]int{1, 2, 3}
    fmt.Println(a1)
    // 如果将元素个数指定为特殊符号...,则表示通过初始化时的给定的值个数来推断数组长度
    a2 := [...]int{1, 2, 3, 4}
    fmt.Println(a2)
    a3 := [...]int{1, 1, 1}
    fmt.Println(a3)
    // 如果声明数组时,只想给其中某几个元素初始化赋值,则使用索引号
    a4 := [4]int{0: 1, 3: 5}
    fmt.Println(a4)
    

    3、访问与修改

    可以通过数组的索引访问数组的值

    a := [4]int{0: 1, 2: 5}
    fmt.Println(a[0])
    fmt.Println(a[2])
    

    同理可通过数组的索引修改数组的值

    a[0] = 10
    a[3] = 20
    fmt.Println(a[0])
    fmt.Println(a[3])
    

    4、指针数组

    可以声明一个指针类型的数组,这样数组中就可以存放指针。注意,指针的默认初始化值为nil

    例如,创建int类型指针的数组

    a := [4]*int{0: new(int), 3: new(int)}
    fmt.Println(a)
    // [0xc00001c2a8 <nil> <nil> 0xc00001c2b0]
    
    // 如果指针地址为空, 是会报空指针错误的, 比如
    // *a[1] = 3
    // panic: runtime error: invalid memory address or nil pointer dereference 
    
    *a[0] = 10
    *a[3] = 20
    fmt.Println(a)
    fmt.Println(*a[0], *a[3])
    
    // 为1赋值
    a[1] = new(int)
    *a[1] = 30
    fmt.Println(a, *a[1])
    

    5、数组拷贝

    Go中,由于数组算是一个值类型,所以可以将它赋值给其它数组
    因为数组类型的完整定义为[n]TYPE,所以数组赋值给其它数组的时候,nTYPE必须相同

    修改a2数组元素的值,不会影响a1数组

    例如:

    a1 := [4]string{"a", "b", "c", "m"}
    a2 := [4]string{"x", "y", "z", "n"}
    a1 = a2
    fmt.Println(a1, a2)
    

    数组赋值给其它数组时,实际上是完整地拷贝一个数组。所以,如果数组是一个指针型的数组,那么拷贝的将是指针数组,而不会拷贝指针所指向的对象

    a1 := [4]*string{new(string), new(string), new(string), new(string)}
    a2 := a1
    fmt.Println(a1, a2)
    
    *a1[0] = "A"
    *a1[1] = "B"
    *a1[2] = "C"
    *a1[3] = "D"
    fmt.Println(*a1[0], *a2[0])
    // A A
    

    6、数组遍历

    range关键字可以对array进行迭代,每次返回一个index和对应的元素值。可以将range的迭代结合for循环对array进行遍历

    a := [4]int{1, 2, 3, 4}
    for i, v := range a {
        fmt.Println(i, v)
    }
    /*
    0 1
    1 2
    2 3
    3 4
    */
    

    7、多维数组

    可以通过组合两个一维数组的方式构成二维数组, 二维数据还是比较常用,

    比如定义坐标, 表示4个坐标(1,1) (2,2) (3,3) (4,4)

    // pos := [4][2]int{{1, 1}, {2, 2}, {3, 3}, {4, 4}}
    fmt.Println(pos)
    // [[1 1] [2 2] [3 3] [4 4]]
    
    // 修改第一点的坐标
    pos[0] = [2]int{10, 10}
    fmt.Println(pos)
    
    // [[10 10] [2 2] [3 3] [4 4]]
    

    8、数组作为函数参数

    Go中的传值方式是按值传递,这意味着给变量赋值、给函数传参时,都是直接拷贝一个副本然后将副本赋值给对方的。这样的拷贝方式意味着:

    • 如果数据结构体积庞大,则要完整拷贝一个数据结构副本时效率会很低
    • 函数内部修改数据结构时,只能在函数内部生效,函数一退出就失效了,因为它修改的是副本对象

    示例

    func TestMain1(t *testing.T) {
    	payload := [4]int{1}
    	fmt.Printf("%p
    ", &payload)  // 0xc00014a040
    	change(payload)  // 0xc00014a060
    }
    
    func change(payload [4]int) {
    	fmt.Printf("%p
    ", &payload)
    	payload[0] = 10
    }
    
  • 相关阅读:
    SpringMVC详解
    java设计模式
    运行时异常与一般异常区别
    oracle基本操作大全
    get post 区别
    hibernate
    Spring框架
    http和https
    JDBC详解
    (转)Entity Framework4.1实现动态多条件查询、分页和排序
  • 原文地址:https://www.cnblogs.com/ssgeek/p/14925152.html
Copyright © 2011-2022 走看看