第五章:Go语言内置容器

Go语言内置容器主要有数组,切片和映射

数组:

定义:具有类型相同长度固定且在对应存放在内存中的一块连续区域的数据结构。
声明数组:var 数组变量名 [数组长度]元素类型
初始化数组:var 数组变量名 = [数组长度]元素类型{元素}
也可不设置数组长度用…代替,go语言编译器根据元素个数来设置数组大小。

func main(){
	var student [3]string
	student = [3]string{"Tom","Allen","Peter"}
	class := [...]string{"一年一一班","一年级二班"}
	fmt.Println(student)
	fmt.Println(calss)		
}
//结果为:
[Tom Allen Peter]
[一年级一班 一年级二班]

切片:

定义:同样表示多个同类型元素的连续集合

切片本身并不存储任何元素,只是对现有数组的引用。即它本身就是一个指针

切片(slice)是对数组一个连续片段的引用(该数组我们称之为相关数组,通常是匿名的),所以切片是一个引用类型,这就导致它们不需要使用额外的内存并且比使用数组更有效率,所以在Go 代码中切片比数组更常用。

切片在内存中的结构包括:地址,长度,容量

地址:切片第一个元素所指向的内存地址,其地址与截取的数组或切片开始位置对应的元素地址相同。
长度:切片中实际元素的个数
容量:从切片的起始元素开始到其底层数组中的最后一个元素的个数

生成方式:从数组生成一个新切片,从切片生成一个新切片,创建一个新切片
func main(){
	student := [...]string{"Tom","Alen","Ming","Peter","Alice"}
	studentSlice1 := student[1:3]              //从数组生成新切片
	studentSlice2 := studentSlice1[:1]         //从切片生成新切片
	fmt.Printf("student数组为:%v,地址为:%v\n",student,&student[1])
	fmt.Printf("studentSlice1切片为:%v,地址为:%v,长度为:%d,容量为:%d\n",studentSlice1,&studentSlice1[0],len(studentSlice1),cap(studentSlice1))
	fmt.Printf("studentSlice2切片为:%v,地址为:%v,长度为:%d,容量为:%d",studentSlice2,&studentSlice2[0],len(studentSlice2),cap(studentSlice2))
}

结果为:
在这里插入图片描述

注意:新生成的切片不包括结束位置对应的元素

声明切片:var 切片变量名 []元素类型
声明未初始化为空切片,默认值为nil
初始化切片:

切片名 := []元素类型{元素} 元素不能为空,即不能用短变量命名空切片
make([]元素类型,切片长度,切片容量(可选))

注意:切片容量值必须大于长度,对切片容量应该有个大概的估值,若容量值过小,对切片多次扩充会造成性能损耗(相当于对底层数组扩充)。

添加元素:

append 当切片不能容纳其他元素时,即切片长度等于容量,再一次append容量会按两倍进行扩充。
注意:如果切片从其他切片或者数组生成(必须是起始位置和结束位置都在数组容量范围内,否则不会覆盖),对新切片元素添加,删除需要考虑对原有数组或切片中数据的影响,覆盖数组对应元素。

删除元素:

删除切片元素没有方法,需要手动将删除点前后的元素连接起来,从而实现对切片中元素的删除。
清空切片所有元素:切片名 = 切片名[0:0]

func main(){
	var student []string   //声明切片
	studentArr := [...]string{"Tom","Alice","Peter"}
	studentSlice := studentArr[:1]
	class := []string{"五年级一班","五年级二班"}   //初始化切片
	teacher := make([]string,1)       //初始化切片
	fmt.Printf("判断student切片是否为空:%v,student:%v\n",student==nil,student)
	fmt.Printf("判断teacher切片是否为空:%v,teacher:%v\n",teacher==nil,teacher)
	fmt.Printf("class切片长度为:%d,容量为:%d\n",len(class),cap(class))
	class = append(class,"五年级三班")
	fmt.Printf("class切片扩充后长度为:%d,容量为:%d\n",len(class),cap(class))
	studentSlice = append(studentSlice,"Ming","Marshall")
	fmt.Println("studentArr为:",studentArr)
	fmt.Println("studentSlice切片为: ",studentSlice)
	studentSlice = append(studentSlice[:1],studentSlice[2])
	fmt.Printf("studentSlice切片删除后为%v,长度为:%d,容量为%d\n",studentSlice,len(studentSlice),cap(studentSlice))
	fmt.Println("studentArr为:",studentArr)
}

结果为:
在这里插入图片描述

映射:一种无序的键值对的集合

声明映射: var map[键类型]值类型
声明未初始化默认为nil
初始化映射, 区别于切片

映射名 := map[键类型]值类型{元素} 元素可为空
make(map[键类型]值类型,map容量(可选))
可以不指定容量,但是多次扩充造成性能损耗。

添加键值对

映射名[键]=值

删除键值对:

delete(map(实例),键)
Go语言中没有为map提供清空所有元素的方法,要想清空的唯一方法是重新定义一个Map。

应用场景:适合存放有关联关系的数据
func main(){
	var student map[string]int        //默认为nil
	class := map[string]int{}         //不为nil
	teacher := make(map[string]int,1)
	teacher["Hu"] = 36
	teacher["Han"] = 45
	fmt.Println("student是否为nil:",student==nil)
	fmt.Println("class是否为nil:",class==nil)
	fmt.Println(teacher)
	delete(teacher,"Hu")
	fmt.Println(teacher)
}

结果为:
在这里插入图片描述

拓展:

make()和new()区别

两者都在堆上分配内存,但是它们的行为不同,适用于不同的类型
make()函数初始化,适用于引用类型,包括切片,映射,通道
new()函数分配内存,适用于值类型,如数组,结构体

range关键字

range通常配合for循环对数组,切片及映射进行迭代
range后接的表达式称为range表达式,迭代时,关键字range会返回两个值,由k,v接收,分别代表当前数组(切片,映射)的索引位置和该位置对应元素值的一份副本

range表达式第一返回值第二返回值
数组元素下标元素值
切片元素下标元素值
映射
通道元素N/A
并发访问map(非并发还是推荐map)

通常对map的操作都是单协程的,如果多个协程并发访问同一个map就会发生异常,所以map不是协程安全的,同一时刻只能有一个协程对map操作。

func main(){
	goMap := make(map[int]int)
	for i := 0;i < 10000;i++{
		go writeMap(goMap,i,i)
		go readMap(goMap,i)
	}
}

func writeMap(goMap map[int]int,key,value int){
	goMap[key] = value
}

func readMap(goMap map[int]int,key int) int{
	return goMap[key]
}

结果为:
在这里插入图片描述
最常见的解决办法是使用sync包对map加锁或者直接在go1.9版本中提供的线程安全map

var lock sync.RWMutex
func main(){
	goMap := make(map[int]int)
	for i := 0;i < 10000;i++{
		go writeMap(goMap,i,i)
		go readMap(goMap,i)
	}
	fmt.Println("读写完成!")
}

func writeMap(goMap map[int]int,key,value int){
	lock.Lock()                   //写之前加锁
	goMap[key] = value
	lock.Unlock()                 //写完解锁
}

func readMap(goMap map[int]int,key int) int{
	lock.Lock()
	value := goMap[key]
	lock.Unlock()
	return value
}

结果为:
在这里插入图片描述
由于加锁会影响程序性能,建议使用效率较高的并发安全的sync.Map

sync.Map特点:

内部通过冗余的数据结构降低加锁对性能的影响
使用前无须初始化,直接声明即可(无须make)
不使用map中的方式进行读取和赋值操作,而是使用Load()和Store()方法
sync.Map没有提供获取map数量的方法,解决方案是通过循环遍历map。

func main(){
	var goMap sync.Map
	for i := 0;i < 10000;i++{
		go writeMap(goMap,i,i)
		go readMap(goMap,i)
	}
	fmt.Println("读写完成!")
}

func writeMap(goMap sync.Map,key,value int){
	goMap.Store(key,value)               //线程安全设置                   
}

func readMap(goMap sync.Map,key int) int {
	res, ok := goMap.Load(key)             //线程安全读取       ---断言
	if ok {
		return res.(int)
	}
	return 0
}

结果同上

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值