• Go语言学习(三)-- 派生数据类型


    1. 数组

    数组是一组具有相同数据类型在内存中有序存储的数据集合

    特点
    • 长度固定,不能修改
    • 赋值和函数传递过程是值复制,涉及到内存 copy,性能低下
    • 数组的定义和使用

    声明数组: var 数组名 [SIZE] 数组变量类型

    初始化数组

    var arr= [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0} 或者 arr:=[5]int{1,2,3,4,5}

    我们也可以通过字面量在声明数组的同时快速初始化数组:

    balance := [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}

    如果数组长度不确定,可以使用 ... 代替数组的长度,编译器会根据元素个数自行推断数组的长度:

    var balance = [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0} 或 balance := [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}

    如果设置了数组的长度,我们还可以通过指定下标来初始化元素:

    // 将索引为 1 和 3 的元素初始化 balance := [5]float32{1:2.0,3:7.0}

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

    • go同样也有多维数组

    声明方式:var variable_name [SIZE1][SIZE2]...[SIZEN] variable_type

    初始化二维数组:

    1. a := [3][4]int{
    2. {0, 1, 2, 3} , /* 第一行索引为 0 */
    3. {4, 5, 6, 7} , /* 第二行索引为 1 */
    4. {8, 9, 10, 11}, /* 第三行索引为 2 */
    5. //注意:以上代码中倒数第二行的 } 必须要有逗号,因为最后一行的 } 不能单独一行,也可以写成这样:
    6. }
    1. a := [3][4]int{
    2. {0, 1, 2, 3} , /* 第一行索引为 0 */
    3. {4, 5, 6, 7} , /* 第二行索引为 1 */
    4. {8, 9, 10, 11}} /* 第三行索引为 2 */

    go语言还支持创建元素数量不一致的多维数组

    1. package main
    2. import "fmt"
    3. func main() {
    4. // 创建空的二维数组
    5. animals := [][]string{}
    6. // 创建三一维数组,各数组长度不同
    7. row1 := []string{"fish", "shark", "eel"}
    8. row2 := []string{"bird"}
    9. row3 := []string{"lizard", "salamander"}
    10. // 使用 append() 函数将一维数组添加到二维数组中
    11. animals = append(animals, row1)
    12. animals = append(animals, row2)
    13. animals = append(animals, row3)
    14. // 循环输出
    15. for i := range animals {
    16. fmt.Printf("Row: %v\n", i)
    17. fmt.Println(animals[i])
    18. }
    19. }

    2. 切片

    切片(slice)是一组具有相同数据类型在内存中有序存储的可扩容的数据集合
    • slice声明方式
    1. var 切片名 []数据类型
    2. var slice []int
    3. make([]数据类型,长度)
    4. var slice []int = make([]int, 10)

    当使用var slice []int 是此时的切片实际上是一个nil,还没有被开辟空间,只是有了一个slice的“引用”。而使用make的方式时会为我们的slice开辟你传入的大小的空间。

    1. func main() {
    2. var slice []int
    3. slice[0] = 123
    4. fmt.Println(slice)
    5. }

    上面的代码是一个错误的代码 在运行的时候会报错越界问题。

     

    • slice的len和cap

    我们可以通过len() cap()函数来求取我们的slice的长度和容量(长度就是我们当前slice中存了多少了元素,而容量是slice最大可以容纳的元素数量。)

    在go源码中当length小于1024的时候是按照2倍来扩容的,当超过1024的时候每次是以前的1.25倍。

    1. func main() {
    2. /*var slice []int
    3. slice[0] = 123
    4. fmt.Println(slice)*/
    5. var s1 = make([]int, 2)
    6. //slice自动扩容
    7. s1 = append(s1, 1)
    8. s1 = append(s1, 2)
    9. s1 = append(s1, 1)
    10. s1 = append(s1, 1)
    11. fmt.Printf("%v\n", s1)
    12. //现在我们来查看len cap
    13. println(len(s1)) //6 0 0 1 2 1 1
    14. println(cap(s1)) //8
    15. }
    •  切片的截取

    切片在截取的时候返回的新的切片是指向原来的切片的内存地址的。感觉类似于浅拷贝,改变截取后的切片的值也会同步的改变原切片的值。

    1. func main() {
    2. //切片的截取
    3. slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    4. //切片名[起始下标:结束下标:容量] 左闭右开 包含起始下标 不包含结束下标
    5. //s := slice[2:7]
    6. //fmt.Println(s)
    7. //s:=slice[2:]
    8. //fmt.Println(s)
    9. //s:=slice[:5]
    10. //fmt.Println(s)
    11. s := slice[2:5:6] //实际容量=容量-起始下标
    12. fmt.Println(s)
    13. //fmt.Println(len(s))
    14. //fmt.Println(cap(s))
    15. s[0] = 333
    16. //切片的截取 是将新的切片指向源切片的内存地址 修改一个会影响另外一个
    17. fmt.Println(s)
    18. fmt.Println(slice)
    19. fmt.Println(unsafe.Sizeof(slice))
    20. fmt.Println(unsafe.Sizeof(s))
    21. }

    利用切片的属性还可以简单的实现一个CRUD

    1. func test6() {
    2. //slice crud
    3. var nums = []int{1, 2, 3, 4, 5}
    4. fmt.Printf("nums = %v\n", nums)
    5. //新增
    6. nums = append(nums, 6)
    7. fmt.Printf("新增后nums = %v\n", nums)
    8. //删除
    9. //比如删除我们切片中的下表为2的元素
    10. s1 := append(nums[:2], nums[3:]...)
    11. fmt.Printf("删除后nums = %v\n", s1)
    12. //改
    13. nums[2] = 222
    14. fmt.Printf("修改后nums = %v\n", nums)
    15. //查找
    16. //给定一个key 查找下标然后返回 找不到返回-1
    17. var key = 3
    18. for i, num := range nums {
    19. if num == key {
    20. fmt.Printf("找到%d了,下表为%d:", key, i)
    21. }
    22. }
    23. }

    3. map

    • map的声明 赋值

    /* 声明变量,默认 map 是 nil */

    var map_variable map[key_data_type]value_data_type

    /* 使用 make 函数 */

    map_variable := make(map[key_data_type]value_data_type)

    1. m1 := map[string]string{
    2. "name": "zhangsan",
    3. "age": "22",
    4. }
    5. fmt.Println(m1)
    6. m2 := make(map[string]int) //m2 == empty map
    7. var m3 map[string]int //m3 == nil
    8. fmt.Print(m2, m3)
    9. fmt.Print("遍历...\n")
    10. for i, v := range m1 {
    11. fmt.Println(i, v)
    12. }
    • 底层基于 Hash 实现,基于 Key-Value,无序的数据集合
    • dict := make(map[T_KEY]T_VALUE) // key 的类型需要具备 == != 操作 (除了slice、map、function的内建类型都可以作为key
    • 函数类型、字典类型和切片类型不能作为 key,不支持的操作类型会导致 panic
    • 检测值是否存在
    1. name := m1["name"]
    2. fmt.Println(name)
    3. //可以返回一个ok值 判断我们的key是否正确
    4. nema, ok := m1["nema"] //找不到这个键 就会返回false
    5. fmt.Print(nema, ok)
    6. //所以可以改进代码
    7. if name, ok := m1["name"]; ok == true {
    8. fmt.Println(name)
    9. }
    • var m map[string]int // nil 类型,添加和修改会导致 panic
    • nil: len/map[key]/delete(m, key) // 可以正常工作
    • map 默认并发不安全,多个 goroutine 写同一个 map,引发竞态错误, go run –race 或者 go build - race
    • map 对象即使删除了全部的 key,但不会缩容空间

    4.指针

    只要将数据存储在内存中都会为其分配内存地址。内存地址使用十六进数据表示。
    内存为每一个字节分配一个 32 位或 64 位的编号(与 32 位或者 64 位处理器相关)。
    可以使用运算符 & (取地址运算符)来获取数据的内存地址。
    1. func main() {
    2. a := 10
    3. //取出变量a在内存的地址
    4. //fmt.Println(&a)
    5. var p *int = &a
    6. //fmt.Println(p)
    7. //fmt.Println(&a)
    8. //通过指针间接修改变量的值
    9. *p = 132
    10. fmt.Println(a)
    11. //const MAX int = 100
    12. //fmt.Println(&MAX)//err 不可以获取常量的内存地址
    13. }
    1. func main() {
    2. //定义指针 默认值为nil 指向内存地址编号为0的空间 内存地址0-255为系统占用 不允许用户读写操作
    3. //var p *int = nil
    4. //*p = 123 //err
    5. //fmt.Println(*p)
    6. //开辟数据类型大小的空间 返回值为指针类型
    7. //new(数据类型)
    8. var p *int
    9. p = new(int)
    10. *p = 123
    11. fmt.Println(*p)
    12. }

  • 相关阅读:
    基础篇04——多表查询
    Redis学习之路(三)--key键操作
    HTTP Catcher(网球)使用教程【五】开启DNS劫持
    「SpringBoot」09 原理解析
    安装tldr
    Java和Python中20个基本编程面试问题
    (21)多线程实例应用:双色球(6红+1蓝)
    redisinsight--基础--2.2--安装--docker安装
    算法通关村——滑动窗口高频问题
    OAuth2.0协议安全学习
  • 原文地址:https://blog.csdn.net/qq_45875349/article/details/127677857