• Golang 数组基础


    数组内部实现和基础功能

    了解Go的数据结构,一般会从数组开始,因为数组是切片和映射的基础数据结构。

    内部实现

    在Go语言中,数组是一个长度固定的数据类型,用于存储一段具有相同的类型的元素连续块。数组的类型是固定统一的(也可以是结构类型)。

    在这里插入图片描述

    在内存中是连续分配的,由于内存连续,CPU能把正在使用的数据缓存更久的时间。而且内存连续很容易计算索引,可以快速迭代数组里的所有元素。
    数组类型相同,且又连续分配,就可以以固定速度索引数组中的任意数据,速度非常快。
    再来一个JavaScript的数组对比,谁快谁慢一目了然。
    在这里插入图片描述

    声明和初始化

    声明数组需要指定内部存储的数据类型,以及需要存储元素的数量(也称为长度)。

    var声明示例
    // 声明一个包含了5个元素的int类型数组
    var arr [5] int
    
    • 1
    • 2

    一旦声明,数组里存储的数据类型和长度就都不能改变了。
    作为一个经常写js的程序员,看到这里有点不淡定了,what ?不能更改了,想加第6个元素怎么办?
    如果要存储更多的元素,就需要先创建一个更长的数组,再把原来的数组里面的值复制到新数组里。
    在Go语言中声明变量时,总会使用对应类型的零值来对变量进行初始化。数组也一样,内部元素会使用对应类型的零值。

    类型零值介绍:

    类型零值
    数值0
    字符串空字符串
    布尔false
    指针nil
    引用类型引用的底层数据结构对应的零值
    快速创建数组并初始化

    数组字面量声明

    // 声明一个包含5个元素的整数数组
    // 赋值并初始化
    arr := [5] int{1,2,3,4,5}
    
    
    • 1
    • 2
    • 3
    • 4

    自动计算声明数组的长度

    // ...替代数组的长度
    // 声明5个int类型的数组,容量由初始化值的数量决定
    arr2 := [...] int {1,2,3,4,5}
    
    • 1
    • 2
    • 3

    声明数组并指定特定元素的值

    // 声明一个有5个元素的值
    // 指定其它中索引1和2的元素的值,其余为零值
    arr3 := [5] int {1: 10, 2: 20}
    // 输出结果: [0 10 20 0 0]
    
    • 1
    • 2
    • 3
    • 4
    数组是值类型

    Go 中的数组是值类型而不是引用类型。
    意味着当数组赋值给一个新的变量时,该变量会得到一个原始数组的一个副本。如果对新变量进行更改,则不会影响原始数组。
    如果想引用传递,可以使用指针。

    使用数组

    访问和赋值

    要访问数组里的元素,可以使用[index]索引来访问。

    arr1 := [5] int {0,1,2,3,4}
    // 修改索引2的值
    arr1[2] = 100
    // 输出结果:[0 1 100 3 4]
    
    • 1
    • 2
    • 3
    • 4

    声明一个所有元素都是指针的数组,使用*运算符可以访问指针指向的值。

    // 声明5个元素的指向整数的数组
    // 整型指针初始化索引为0和1的数组元素
    arr2 := [5] *int { 0: new(int), 1: new(int)}
    //当前打印arr2: [0xc00001c098 0xc00001c0d0   ]
    
    //给索引0和1的元素赋值
    *arr2[0] = 100
    *arr2[1] = 200
    // 再次打印arr2
    fmt.Println(arr2)
    // 打印结果:[0xc0000a6058 0xc0000a6090   ]还是和赋值前一样,返回的是指针地址
    fmt.Println(*arr2[0], *arr2[1])
    // 打印指针结果:100, 200
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    同类型同长度的可以相互赋值

    var arr4 [5]string
    arr42 := [5]string{"a", "b", "c", "d", "e"}
    arr4 = arr42
    fmt.Println(arr4, arr42)
    // 输出结果:[a b c d e] [a b c d e]    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    指针数组复制,只会复制指针地址,而不会复制指针所指向的值,也就是说值变了,对应复制的数组值也会相应改变。

    // 声明3个元素字符串的指针数组
    var a1 [3]*string
    // 声明第2个指针数组
    a2 := [3]*string{new(string), new(string), new(string)}
    // 给指针2数组赋值
    *a2[0] = "apple"
    *a2[1] = "huawei"
    *a2[2] = "oppo"
    // 打印对比指针数组,a1是nil
    fmt.Println(a1, a2) // [  ] [0xc000104220 0xc000104230 0xc000104240]
    a1 = a2
    // a2赋给a1后,打印对比,指针地址一样
    fmt.Println(a1, a2) // [0xc000040250 0xc000040260 0xc000040270] [0xc000040250 0xc000040260 0xc000040270]
    // 改变*a2[0]的值 ,看看a1是否跟着变化
    *a2[0] = "change apple"
    // 指针地址没有变,但*a1[0]的实际值也跟着变化了
    fmt.Println(*a1[0], *a2[0]) // change apple change apple
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    如图示:
    改变*a1[0]的指针地址,0xc000040250地址处的赋值变更。
    在这里插入图片描述

    遍历数组元素
    for循环遍历

    代码示例:

    a1 := [...]string{"apple", "huiwei", "oppo", "xiaomi"}
    for i := 0; i <= len(a1); i++ {
    	fmt.Printf("elem i %d:, value: %s", i, a1[i])
    	fmt.Println()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    range遍历

    代码示例:

    for i, v := range a1 {
    		fmt.Printf("elem i %d, value :%s \n", i, v)
    	}
    
    • 1
    • 2
    • 3

    输出结果与上面的for循环一致。

    多维数组

    数组本身只有一个维度,多维数组就是指多个数组组成创建多维数组。跟其它语言一样。
    声明二维数组示例

    // var 零值声明
    var a1 [2][2]int
    fmt.Println(a1)
    
    // 字面量声明
    a2 := [2][2]int{{1, 2}, {21, 22}}
    fmt.Println(a2)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    使用方法也类似,[index]索引访问和赋值使用。
    多维数组复制也一样,需要相同的类型和相同的长度

    数组在函数中的传递

    在Go语言中,函数之间的变量传递都是以值的方式传递的。
    如果变量是数组,意味着整个数组都会完整的复制传递给函数。
    如果数组非常大,在值传递过程中会非常的消耗性能,不断的开辟内存空间来复制存储。
    最佳的实现方式是使用指针传递。
    示例:

    
    // 指针传递函数
    func arrShowValues2(arr *[1e6]int) {
    	arr[0] = 201
    	fmt.Println(arr[0])
    }
    func arrShowValues1(arr [1e6]int) {
    	arr[0] = 101
    	fmt.Println(arr[0])
    }
    func arrFn2() {
    	// 100万个int类型的数据,分配8MB内存
    	var arr1 [1e6]int
    	// 值传递
    	arrShowValues1(arr1) // 101
    	// 指针传递
    	arrShowValues2(&arr1)  // 201
    }
    
    // hello word
    func main() {
    	arrFn2()
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    两者的性能不在一个量级。

  • 相关阅读:
    全局光照RSM
    electron学习笔记
    关于plt.scatter()的使用
    java-springboot基于机器学习得心脏病预测系统 的设计与实现-计算机毕业设计
    使用eclipce ,将java项目打包成jar包
    博客摘录「 Python面试宝典」2024年2月20日
    centos 7 中没有iptables 和service iptables save 指令使用失败问题解决方案
    探索Java设计模式:中介者模式
    .net SDK 版本信息查询
    数据结构与算法(C语言版)P5---栈
  • 原文地址:https://blog.csdn.net/ziyi813/article/details/127914107