Go中的map和指针
本文参考:https://www.liwenzhou.com/posts/Go/08_map/
MAP(映射)
Go语言中提供的映射关系容器为map,其内部使用散列表(hash)实现。(类似于Python中的字典dict)
映射概述
map是一种无序的基于key-value的数据结构,Go语言中map是引用类型,必须初始化后才能使用。
创建map
Go语言中map的定义语法如下:
map[keyType]valueType
//keyType 表示键的类型
//valueType表示键对应值的类型
map类型的变量默认初始值为nil,需要使用make()函数来分配内存。
make(map[keyType]ValueType,cap)
// cap表示map的容量,不指定时默认为0,但是我们应该在初始化map的时候就为其指定一个合适的容量
示例:
func main(){
// 在声明的时候填充元素
userInfo := map[string]string{
"username":"Negan"
"password":"mima"
}
// 使用内建函数make()创建
scoreMap:=make(map[string]int,8)
scoreMap["张三"] = 90
scoreMap["李四"] = 100
fmt.Println(scoreMap) //map[张三:90 李四:100]
fmt.Println(scoreMap["李四"]) //100
fmt.Printf("type of a:%T\n",scoreMap) //type of a:map[string]int
//创建一个string为键,值为任意类型的map
userMap := make(map[string]interface{})
userMap["name"] = "Negan"
userMap["age"] = 68
}
判断某个键是否存在
Go语言中有个判断map中键是否存在的特殊写法
value,ok:=map[key]
示例:
func main(){
scoreMap := make(map[string]int)
scoreMap["张三"] = 90
scoreMap["李四"] = 100
// 如果key存在ok为true,v为对应的值,不存在ok为false,v为值类型的零值
v,ok := scoreMap["张三"]
if ok{
fmt.Println(v)
}else{
fmt.Println("查无此人")
}
}
map的遍历
range关键字对map遍历,可以同时得到键值对的key和value,也可仅仅得到key
func main(){
scoreMap := make(map[string]int)
scoreMap["张三"] = 90
scoreMap["李四"] = 100
// 遍历key和value
for k,v := range scoreMap{
fmt.Println(k,v)
}
//仅仅遍历key
for k := range scoreMap{
fmt.Println(k)
}
}
- 注意:遍历map时的元素顺序与添加键值对的顺序无关。
按照指定循序遍历map
func main(){
rand.Seed(time.Now().UnixNano()) // 初始化随机数种子
scoreMap := make(map[string]int,200)
for i:=0;i<100;i++{
key := fmt.Sprintf("stu%02d",i) // 生成stu开头的字符串
value := rand.Intn(100) // 生成0-99的随机整数
scoreMap[key] = value
}
// 取出map中所有的key存放到切片keys中
keys := make([]string,0,200)
for key:=range scoreMap{
keys = append(keys, key)
}
// 对切片进行排序
sort.Strings(keys)
// 按照排序后的key遍历map
for _,key := range keys{
fmt.Println(key, scoreMap[key])
}
}
使用delete()函数删除键值对
使用delete()内建函数从map中删除一组键值对,delete()函数的格式如下:
delete(map,key)
// map:表示要删除键值对的map
// key:表示要删除的键值对的键
示例:
func main(){
scoreMap := make(map[string]int)
scoreMap["张三"] = 90
scoreMap["李四"] = 100
delete(scoreMap,"张三") // 将张三:90从map中删除
fmt.Println(scoreMap)
}
元素为map类型的切片
下面代码延时了切片中的元素为map类型时的操作:
func main(){
var mapSlice = make([]map[string]string,3)
for index,value := range mapSlice{
fmt.Printf("index:%d value:%v\n",index,value)
}
fmt.Println("after init")
// 对切片中的map元素进行初始化
mapSlice[0] = make(map[string]interface{},10)
mapSlice[0]["name"] = "Negan"
mapSlice[0]["age"] = 68
mapSlice[0]["hobby"] = "棒球"
for index,value:= range mapSlice{
fmt.Printf("index:%d value:%v\n",index, value)
}
}
值为切片类型的map
下面代码演示map中值为切片类型的操作
func main{
sliceMap := make(map[string][]string,3)
fmt.Println(sliceMap)
fmt.Println("after init")
key := "中国"
value,ok := sliceMap[key]
if !ok{
value = make([]string,0,2)
}
value = append(value, "北京","上海","西安")
sliceMap[key = value
fmt.Println(sliceMap)
}
练习题
1、写一个程序,统计一个字符串中每个单词出现的次数。比如:“how do you do”中how=1 do=2 you=1。
func main(){
s := “how do you do”
s_sclice := strings.Split(s," ") // 分割字符串
count_map := make(map[string]int)
for key := range s_sclice{
_,ok:=count_map[key]
if !ok{
count_map[key] = 1
}else{
count_map[key] += 1
}
}
fmtPrintln(count_map)
}
指针
Go中的指针区别于C/C++中的指针,Go语言中的指针不能进行便宜和运算,是安全指针。
任何数据载入内存后,在内存都有它们的地址,这就是指针。为了保存一个数据在内存中的地址,我们就需要指针变量。指针变量的值就是内存中的一块地址编号。
比如:“卑鄙是卑鄙者的通行证,高尚是高尚的墓志铭”,想把它写入程序中,程序一启动,这句话是要加载到内存(假设内存地址ox123456),在程序中把这句诗赋值给变量A,把内存地址赋值给变量B。这时候变量B就是一个指针变量。通过变量A和B都能找到这句诗。
Go语言中的指针不能进行偏移和运算,因此Go语言中的指针操作非常简单,只需要记住两个符号,&(取地址)和*(根据地址取值)
指针地址和指针类型
每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置。
Go语言中使用&字符放在变量前面对变量进行“取地址操作”。Go语言中值类型(int、float、bool、string、array、struct)都有对应的指针类型。
如*int、*int64、*string等。
取变量指针语法如下:
ptr := &v // v的类型为T
// v:代表被取地址的变量,类型为T
// ptr:用于接收地址的变量,ptr的类型就是*T,称为T的指针类型。
示例:
func main(){
a := 10
b := &a
fmt.Printf("a:%d ptr:%p\n", a, &a) //a:10 ptr:0xc00001a078
fmt.Printf("b:%p type:%T\n", b, b) //b:0xc00001a078 type:*int
fmt.Println(&b) // 0xc00000e018
}

指针取值
在对普通变量使用&操作符取地址后获得这个变量的指针,然后可以对指针使用*操作。也就是指针取值。
func main(){
// 指针取值
a := 10
b := &a // 取变量a的地址,将指针保存到b
fmt.Printf("type of b:%T\n",b) // type of b:*int
c := *b // 指针取值(根据指针去内存取值)
fmt.Printf("type of c:%T\n", c) // type of c:int
fmt.Printf("value of c:%v\n", c) // value of c:10
}
总结:取地址操作符&和取值操作符*是一对互补操作符。
&取出地址,*根据地址取出地址指向的值
变量、指针地址、指针变量、取地址、取值的相互关系和特性如下:
- 对变量进行取地址(&)操作,可以获取这个变量的指针变量
- 指针变量的值是指针地址
- 对指针变量进行取值(*)操作,可以获得指针变量指向的原变量的值。
指针传值示例
func modify1(x int){
x = 100
}
func modify2(x *int){
*x = 100
}
func main(){
a := 10
modify1(a)
fmt.Println(a) // 10
modify2(&a)
fmt.Println(a) // 100
}
new和make
在Go语言中对于引用类型的变量,在使用时不仅要声明它,还要为它分配内存空间,否则我们的值没办法进行存储,而对于值类型的声明,不需要分配内存空间,是因为它们在声明的时候已经默认分配好了内存空间。
func main(){
var a *int
*a = 100
fmt.Println(*a) // 引发panic
var b map["string"]int
b["age"] = 10
fmt.Println(b) // 引发panic
}
new
new是一个内置函数,函数签名如下:
func new(Type) *Type
// Type 表示类型,new函数只接受一个参数,这个参数是一个类型
// *Type 表示类型指针,new函数返回一个指向该类型内存地址的指针
new函数不太常用,使用new函数得到的是一个类型的指针,并且该指针对应的值为该类型的零值。
func main(){
a := new(int)
b := new(bool)
fmt.Prtintf("%T\n",a) // *int
fmt.Printf("%T\n", b) // *bool
fmt.Printf(*a) // 0
fmt.Printf(*b) //false
}
刚开始我们的实例代码中var a *int只是声明一个指针变量a,但是并没有初始化,指针座位引用类型,需要初始化后才能拥有内存空间,才可以给它进行复制。
func main(){
var a *int // 声明a为int类型的指针
a = new(int) // 分配内存空间
*a = 10
fmt.Println(*a)
}
make
make也是用来分配内存的,区别于new,它只用于slice,map以及chan的内存创建。而且返回的类型就是这三个类型的本身。而不是它们的指针类型。因为这三种类型本身就是引用类型。所以没必要返回它们的指针类型。
func make(t Type, size ...IntegerType) Type
make函数是无可替代的,我们在使用slice、map以及channel的时候,都需要使用make进行初始化,然后才可以对它们进行操作。
刚开始的示例中var b map[string]int只是生命变量b是一个map类型的变量。不能直接进行给它复制,需要使用make函数进行操作之后,才能对其进行键值对赋值。
func main(){
var b map[string]int
b = make(map[string]int,10)
b["age"] = 10
fmt.Println(b)
}
new和make的区别
两者都是用来做内存分配的。
make只用于slice、map、以及channel的初始化,返回的还是这三个引用类型本身
new用于类型的内存分配,并且内存对应的值为类型零值,返回的是指向类型的指针。
本文参考:https://www.liwenzhou.com/posts/Go/08_map/
Go中的map和指针的更多相关文章
- Java集合中的Map接口
jdk1.8.0_144 Map是Java三种集合中的一种位于java.util包中,Map作为一个接口存在定义了这种数据结构的一些基础操作,它的最终实现类有很多:HashMap.TreeMap.So ...
- MFC关于多线程中传递窗口类指针时ASSERT_VALID出错的另类解决 转
MFC关于多线程中传递窗口类指针时ASSERT_VALID出错的另类解决 在多线程设计中,许多人为了省事,会将对话框类或其它类的指针传给工作线程,而在工作线程中调用该类的成员函数或成员变量等等. ...
- Eigen中的map
Map类用于通过C++中普通的连续指针或者数组 (raw C/C++ arrays)来构造Eigen里的Matrix类,这就好比Eigen里的Matrix类的数据和raw C++array 共享了一片 ...
- golang中的map
1. 声明与初始化 // map的声明与初始化 userInfo := map[string]string{"name": "马亚南", "age&q ...
- 【转】hive优化之--控制hive任务中的map数和reduce数
一. 控制hive任务中的map数: 1. 通常情况下,作业会通过input的目录产生一个或者多个map任务. 主要的决定因素有: input的文件总个数,input的文件大小,集群设置 ...
- Map java中的map 如何修改Map中的对应元素
Map java中的map 如何修改Map中的对应元素 Map以按键/数值对的形式存储数据,和数组非常相似,在数组中存在的索引,它们本身也是对象. Map的接口 Map ...
- Java中Set Map List 的区别
java中set map list的区别: 都是集合接口 简要说明 set --其中的值不允许重复,无序的数据结构 list --其中的值允许重复,因为其为有序的数据结构 map--成对的数据结构 ...
- c++中的引用与指针的区别
http://blog.csdn.net/lyd_253261362/article/details/4323691 c++中的引用与指针的区别 ★ 相同点: 1. 都是地址的概念: 指针指向一块内存 ...
- Java中遍历Map集合的四种方法
在Java中如何遍历Map对象 How to Iterate Over a Map in Java 在java中遍历Map有不少的方法.我们看一下最常用的方法及其优缺点. 既然java中的所有map都 ...
- java8中的map和reduce
java8中的map和reduce 标签: java8函数式mapreduce 2014-06-19 19:14 10330人阅读 评论(4) 收藏 举报 分类: java(47) FP(2) ...
随机推荐
- eslint-plugin-vue配置中文翻译
eslint-plugin-vue配置中文翻译 由于 ellint 配置太多,很多小伙伴不知道其功能是什么,在此做个记录. //更详细的配置文档请参考:https://github.com/vuejs ...
- 基础指令:mkdir、ls、cd、pwd、touch、rm、mv、cp、echo、cat、关机与重启
目录 1. 创建目录 2. 查看目录内容 3. 进入指定目录(传送) 4. 显示当前所在位置 5. 创建文件 6. 删除文件或目录 7. 移动文件 8. 复制文件或目录 9. echo输出信息到屏幕 ...
- Linux性能分析-CPU上下文切换
前言 在Linux性能分析-平均负载中,提到过一种导致平均负载升高的情况,就是有大量进程或线程等待cpu调度. 为什么大量进程或者线程等待CPU调度会导致负载升高呢? 当大量进程或者线程等待调度时,c ...
- 【C#】SuperSocket配置启动UDP服务器
SuperSocket配置UDP服务器 零.需求 两个设备局域网联机,需要用广播自动搜寻,而SuperSocket1.6的默认AppServer使用的是TCP,但只有UDP才支持广播. 一.解决 推荐 ...
- AspNetCore Json序列化设置
AspNetCore 中的Json序列化处理已经默认使用Newtonsoft.Json库了... 比如像MVC中: public I 不过使用过程中会发现一些问题,其实这算默认设置吧: Json序列化 ...
- postgresql的日期函数
一个to_char干完所有的活.包括日期的转换 函数 返回类型 描述 实例 to_char(timestamp, text) text 将时间戳转换为字符串 to_char(current_times ...
- C#连接小智服务器并将音频解码播放过程记录
前言 最近小智很火,本文记录C#连接小智服务器并将音频解码播放的过程,希望能帮助到对此感兴趣的开发者. 如果没有ESP-32也想体验小智AI,那么这两个项目很适合你. 1.https://github ...
- 根据二叉树的前序和中序构建树,并按层次输出(C++)vector存树
L2-006 树的遍历 #include <bits/stdc++.h> #define int long long using namespace std; #define endl ' ...
- 快速定位MySQL 8.0中的慢查询语句详细步骤
步骤一.启用慢查询日志 慢查询日志是MySQL记录执行时间超过指定阈值的SQL语句 配置慢查询日志 在MySQL配置文件(如my.cnf或my.ini)中设置以下参数: slow_query_log: ...
- eolinker校验规则之 Json Path定位:返回值每一项数组内值校验
如下图,获取H5首页菜单,验证菜单名是否正确 找到对应的接口,查看返回数据,菜单名字存放在TabBar下的3个数组内 Eolinker传统的JSON参数定位(json结构定位)只能校验第一个数组内的p ...