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

初始化二维数组:

a := [3][4]int{  
    {0, 1, 2, 3} ,   /*  第一行索引为 0 */
    {4, 5, 6, 7} ,   /*  第二行索引为 1 */
    {8, 9, 10, 11},   /* 第三行索引为 2 */

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

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

package main

import "fmt"

func main() {
    // 创建空的二维数组
    animals := [][]string{}

    // 创建三一维数组,各数组长度不同
    row1 := []string{"fish", "shark", "eel"}
    row2 := []string{"bird"}
    row3 := []string{"lizard", "salamander"}

    // 使用 append() 函数将一维数组添加到二维数组中
    animals = append(animals, row1)
    animals = append(animals, row2)
    animals = append(animals, row3)

    // 循环输出
    for i := range animals {
        fmt.Printf("Row: %v\n", i)
        fmt.Println(animals[i])
    }
}

2. 切片

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

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

func main() {
	var slice []int
	slice[0] = 123
	fmt.Println(slice)
}

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

 

  • slice的len和cap

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

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

func main() {
	/*var slice []int
	slice[0] = 123
	fmt.Println(slice)*/

	var s1 = make([]int, 2)
	//slice自动扩容
	s1 = append(s1, 1)
	s1 = append(s1, 2)
	s1 = append(s1, 1)
	s1 = append(s1, 1)
	fmt.Printf("%v\n", s1)
	//现在我们来查看len cap
	println(len(s1)) //6  0 0 1 2 1 1
	println(cap(s1)) //8
}
  •  切片的截取

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

func main() {
	//切片的截取
	slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

	//切片名[起始下标:结束下标:容量]  左闭右开 包含起始下标 不包含结束下标
	//s := slice[2:7]
	//fmt.Println(s)
	//s:=slice[2:]
	//fmt.Println(s)
	//s:=slice[:5]
	//fmt.Println(s)
	s := slice[2:5:6] //实际容量=容量-起始下标
	fmt.Println(s)
	//fmt.Println(len(s))
	//fmt.Println(cap(s))
	s[0] = 333
	//切片的截取 是将新的切片指向源切片的内存地址  修改一个会影响另外一个
	fmt.Println(s)
	fmt.Println(slice)

	fmt.Println(unsafe.Sizeof(slice))
	fmt.Println(unsafe.Sizeof(s))
}

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

func test6() {
	//slice crud

	var nums = []int{1, 2, 3, 4, 5}
	fmt.Printf("nums = %v\n", nums)
	//新增
	nums = append(nums, 6)
	fmt.Printf("新增后nums = %v\n", nums)
	//删除
	//比如删除我们切片中的下表为2的元素
	s1 := append(nums[:2], nums[3:]...)
	fmt.Printf("删除后nums = %v\n", s1)
	//改
	nums[2] = 222
	fmt.Printf("修改后nums = %v\n", nums)
	//查找
	//给定一个key 查找下标然后返回 找不到返回-1
	var key = 3
	for i, num := range nums {
		if num == key {
			fmt.Printf("找到%d了,下表为%d:", key, i)
		}
	}

}

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)

m1 := map[string]string{
		"name": "zhangsan",
		"age":  "22",
	}
	fmt.Println(m1)

	m2 := make(map[string]int) //m2 == empty map

	var m3 map[string]int //m3 == nil

	fmt.Print(m2, m3)

	fmt.Print("遍历...\n")
	for i, v := range m1 {
		fmt.Println(i, v)
	}
  • 底层基于 Hash 实现,基于 Key-Value,无序的数据集合
  • dict := make(map[T_KEY]T_VALUE) // key 的类型需要具备 == != 操作 (除了slice、map、function的内建类型都可以作为key
  • 函数类型、字典类型和切片类型不能作为 key,不支持的操作类型会导致 panic
  • 检测值是否存在
name := m1["name"]
fmt.Println(name)
//可以返回一个ok值 判断我们的key是否正确
nema, ok := m1["nema"] //找不到这个键 就会返回false
fmt.Print(nema, ok)
//所以可以改进代码
if name, ok := m1["name"]; ok == true {
    fmt.Println(name)
}
  • 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 位处理器相关)。
可以使用运算符 & (取地址运算符)来获取数据的内存地址。
func main() {
	a := 10
	//取出变量a在内存的地址
	//fmt.Println(&a)
	var p *int = &a
	//fmt.Println(p)
	//fmt.Println(&a)
	//通过指针间接修改变量的值
	*p = 132
	fmt.Println(a)

	//const MAX int = 100
	//fmt.Println(&MAX)//err 不可以获取常量的内存地址
}
func main() {
	//定义指针 默认值为nil 指向内存地址编号为0的空间  内存地址0-255为系统占用 不允许用户读写操作
	//var p *int = nil
	//*p = 123 //err
	//fmt.Println(*p)

	//开辟数据类型大小的空间 返回值为指针类型
	//new(数据类型)
	var p *int
	p = new(int)
	*p = 123
	fmt.Println(*p)
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小小小小关同学

你的支持就是我的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值