GO语言实战四 数组、切片、映射

一、数组

Go 语言有 3 种数据结构可以让用户 管理集合数据:数组、切片和映射。
先学习数组

数组内部实现和基础功能

  1. 内部实现
    定义:数组是一个长度固定的数据类型,用于存储一段具有相同的类型的元素的连 续块。数组存储的类型可以是内置类型,如整型或者字符串,也可以是某种结构类型
    特点:内存连续、迭代更快速,由于长度固定 使用较少
    在这里插入图片描述
  2. 声明和初始化
// 声明一个包含 5 个元素的整型数组
// 用具体值初始化每个元素
array := [5]int{10, 20, 30, 40, 50}
// 声明一个整型数组 ...替代数组的长度
// 用具体值初始化每个元素
// 容量由初始化值的数量决定
array := [...]int{10, 20, 30, 40, 50}
  1. 使用数组
    由于数组内存是连续的,所以数组是效率很高的数据结构,访问任意元素效率很高,使用[]运算符
array := [5]int{1,2,3,4,5}
array[2] = 3
数组互相赋值
var arr1 [2]string
arr2 :=[2]string{'red','blue'}
arr1 = arr2

数组变量的类型包括数组长度和每个元素的类型。只有这两部分都相同的数组,才是类型相同的数组,才能互相赋值
4. 多维数组

// 声明一个二维整型数组,两个维度分别存储 4 个元素和 2 个元素
    var array [4][2]int
// 使用数组字面量来声明并初始化一个二维整型数组
array := [4][2]int{{10, 11}, {20, 21}, {30, 31}, {40, 41}}
// 声明并初始化外层数组中索引为 1 个和 3 的元素 
array := [4][2]int{1: {20, 21}, 3: {40, 41}}
// 声明并初始化外层数组和内层数组的单个元素 
array := [4][2]int{1: {0: 20}, 3: {1: 41}}

  1. 函数间传递数组
    根据内存和性能来看,在函数间传递数组是一个开销很大的操作。在函数之间传递变量时, 总是以值的方式传递的。如果这个变量是一个数组,意味着整个数组,不管有多长,都会完整复 制,并传递给函数,因此最好传入数组的指针

二、切片

切片是一种数据结构,这种数据结构便于使用和管理数据集合。切片是围绕动态数组的概念 构建的,可以按需自动增长和缩小。切片的动态增长是通过内置函数 append 来实现的。这个函 数可以快速且高效地增长切片。还可以通过对切片再次切片来缩小一个切片的大小。因为切片的 底层内存也是在连续块中分配的,所以切片还能获得索引、迭代以及为垃圾回收优化的好处

  1. 内部实现
    切片是一个很小的对象,对底层数组进行了抽象,并提供相关的操作方法,
    三个字段
  • 指向底层数组的指针
  • 切片访问的元素的个数
  • 切片允许增长到的元素个数 即容量
    在这里插入图片描述
  1. 创建和初始化
  • make 和切片字面量
//使用长度和容量声明整型切片
  s1 := make([]int, 2, 10)
  s2 := make([]int, 2)
  fmt.Printf("s1=>%v,长度%d,容量%d,s2=>%v", s1, len(s1), cap(s1), s2)
  输出:s1=>[0 0],长度2,容量10,s2=>[0 0]
  
  // 使用切片字面量声明切片
  s4 := []string{"red", "blue", "green"}
  fmt.Println(s4)//[red blue green]

容量不能小于长度
在这里插入图片描述
声明数组和切片的不同 , [] 指定长度是数组 否则是切片

// 创建有 3 个元素的整型数组
    array := [3]int{10, 20, 30}
// 创建长度和容量都是 3 的整型切片 
slice := []int{10, 20, 30}
  • nil 切片和空切片
//声明nil切片
var s5 []int
   fmt.Println(s5 == nil) //true
//声明空切片
   s6 := make([]int, 0)
   fmt.Println(len(s6))//0

共享底层数组
在这里插入图片描述

  1. 切片增长
	s1 := []int{0, 1, 2}
	fmt.Printf("%v %p %d", s1, s1, cap(s1)) //[0 1 2] 0xc000084000 3
	l()
	s1 = append(s1, 3)
	fmt.Printf("%v %p %d", s1, s1, cap(s1))	//[0 1 2 3] 0xc000066060 6

在这里插入图片描述

函数 append 会智能地处理底层数组的容量增长。
容量小于 1000 成倍地增加容量。
超过 1000,增长因子=1.25,每次增加 25% 的容量。
随着语言的演化,这种增长算法可能会有所改变
  1. 迭代切片
range 迭代
	s1 := []int{1, 2, 3}
	for i, v := range s1 {
		fmt.Printf("%d ==> %d\n", i, v)
	}
	//0 ==> 1 1 ==> 2 2 ==> 3

//使用 ——下划线忽略 不需要的值
	slice := []int{10, 20, 30, 40}
// 迭代每个元素,并显示其值
for _, value := range slice {
fmt.Printf("Value: %d\n", value) 
}
Value: 10
Value: 20
Value: 30
Value: 40

range 创建了每个元素的副本,而不是直接返回对该元素的引用
// 创建一个整型切片
// 其长度和容量都是 4 个元素
slice := []int{10, 20, 30, 40}
// 迭代每个元素,并显示值和地址
for index, value := range slice {
fmt.Printf("Value: %d Value-Addr: %X ElemAddr: %X\n", value, &value, &slice[index])
}

Output:
Value: 10 Value-Addr: 10500168 ElemAddr: 1052E100 
Value: 20 Value-Addr: 10500168 ElemAddr: 1052E104
Value: 30 Value-Addr: 10500168 ElemAddr: 1052E108
Value: 40 Value-Addr: 10500168 ElemAddr: 1052E10C

因为迭代返回的变量是一个迭代过程中根据切片依次赋值的新变量,所以 value 的地址总 是相同的。要想获取每个元素的地址,可以使用切片变量和索引值
总结range 迭代

  • range 创建了每个元素的副本,而不是直接返回对该元素的引用
  • 关键字 range 总是会从切片头部开始迭代
for迭代
	s1 := []int{1, 2, 3, 4}
	for i := 1; i < len(s1); i++ {
		fmt.Printf("%d ==> %d\n", i, s1[i])
	}
	1 ==> 2  2 ==> 3  3 ==> 4

映射

  1. 定义
    映射是一种数据结构,用于存储一系列无序的键值对。
    映射里基于键来存储值。图 4-23 通过一个例子展示了映射里键值对
    在这里插入图片描述

  2. 内部实现

      映射是一个集合,可以使用类似处理数组和切片的方式迭代映射中的元素。但映射是无序的
      集合,意味着没有办法预测键值对被返回的顺序。即便使用同样的顺序保存键值对,每次迭
      代映射的时候顺序也可能不一样。无序的原因是映射的实现使用了**散列表**
    

在这里插入图片描述

  1. 创建和初始化
make创建
	m1 := make(map[string]int)
   fmt.Println(m1)
   m2 := map[string]string{"color": "red", "name": "dragon"}
   fmt.Println(m2)
   字面量创建
   m3 :=map[int][]string{}
   m3[1] = []string{"adfsa", "dfads"}
   fmt.Println(m3)//map[1:[adfsa dfads]]
   // m3 := map[[]string]int{}//invalid map key type []string go
  1. 使用映射
//赋值
	m1 := make(map[string]string)
   m2 := map[string]string{}

   m1["color"] = "blue"
   m2["name"] = "cdb"
   fmt.Println(m1, m2)//map[color:blue] map[name:cdb]

//对 nil 映射赋值时的语言运行时错误
   var colors map[string]string
   colors["color1"] = "red"
   fmt.Println(colors)//panic: assignment to entry in nil map

从映射获取值并判断键是否存在
   value, exists := m1["color"]
   value1, exists1 := m1["aa"]

   if exists {
   	fmt.Println(value)
   }
   if exists1 {
   	fmt.Println(value1)
   } else {
   	fmt.Println("aa 未设置")//aa 未设置

   }
   
从映射获取值,并通过该值判断键是否存在
   value2 := m1["color"]
   value3 := m1["ds"]
   if value2 != "" {
   	fmt.Println(value2) //blue
   }
   if value3 != "" {
   	fmt.Println(value2)
   } else {
   	fmt.Println("ds 未设置" + value3) //ds 未设置
   }

删除一项
   //删除
   delete(m1, "color")
   fmt.Println(m1)

  1. 迭代
	m1 := map[string]string{"color": "red", "name": "kin", "age": "12"}
	fmt.Println(m1)

	for key, value := range m1 {
		fmt.Printf("%s=> %s\n", key, value)
	}
	//color=> red name=> kin age=> 12
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值