Go语言中提供的映射关系容器为map

  1. Go中内置类型,其内部使用散列表(hash)实现,为引用类型

  2. 无序键值对(key-value)集合,通过key(类似索引)快速检索数据

  3. 必须初始化才能使用

一、map

1.1 map是什么?

Map是一种数据结构,是一个集合,用于存储一系列无序的键值对。

基于键存储的,可以快速快速检索数据,键指向与该键关联的值

1.2 map的内部实现

Map存储的是什么?Map存储的是无序的键值对集合。

Map基于什么?基于散列表(hash表),故每次迭代Map,打印的key和value是无序的,每次迭代不一样

Map散列表特点是?包含一组桶,每次存储和查找键值对的时候,都要先选择一个桶。

如何选择桶?把指定的键传给散列函数,就可索引到相应的桶,进而找到对应的键值。

这种方式的好处是?存储的数据越多,索引分布越均匀,所以我们访问键值对的速度也就越快

1.3 map声明和定义

Go语言中 map的定义语法如下:

map[KeyType]ValueType

其中,

  • KeyType:表示键的类型。
  • ValueType:表示键对应的值的类型。

例子:

//var a map[key的类型]value类型
var a map[string]int
var b map[int]string
var c map[float32]string

注意:

1.声明是不会分配内存的,需要make初始化

2.map必须初始化才能使用,否则panic。

3.map中声明value是什么类型,就存什么类型,混合类型自身不支持(Go是强类型语言),interfice支持存混合类型数据。

map类型的变量未初始化前默认为nil,所以需要使用make分配map内存,然后才能使用,不然会panic(异常)

实例如下:

实例1-1

package main

import (
"fmt"
) func main() {
var a map[string]int
if a == nil { //map未初始化,其初始默认为nil
fmt.Println("map is nil. Going to make one.")
a = make(map[string]int)
}
}

执行结果如下:

另外也验证一下不初始化跑出panic

实例如下:

实例1-2

package main

import (
"fmt"
) func main() {
var user map[string]int
user["abc"] = 38
fmt.Println(user)
}

执行结果如下:

已经抛出了panic,所以必须要初始化。

1.4 map初始化

1.4.1 方法1 make

map类型的变量默认初始值为nil,需要使用make()函数来分配内存。语法为:

make(map[KeyType]ValueType, [cap])

其中cap表示map的容量,该参数虽然不是必须的,但是我们应该在初始化map的时候就为其指定一个合适的容量。

实例:

package main

import (
"fmt"
) func main() {
var user map[string]int = make(map[string]int, 5000) //初始化时可以指定容量也可以不指定,指定的话可以提升性能
//user := make(map[string]int) //也可以写成这样,多种方式
user["abc"] = 38
fmt.Println(user)
}

执行结果:

1.4.2 方法2 声明时进行初始化(借助常量)

m:= map[string]int{"张三":21}
m2:= map[string]int{"张三":21,"李四":22}
m3:= map[string]int{} //空map

实例:

package main

import (
"fmt"
) func main() {
a := map[string]int{
"steve": 12000,
"jamie": 15000,
}
a["mike"] = 9000
fmt.Println("a map contents:", a)
}

执行结果:

1.6 map扩容

map扩容实际上类似切片扩容就是:

map本来的容量是4,现在容量不够了,map内部自动扩容,比如说扩容到8,其在底层的机制就是将旧的内存地址中的4个元素拷贝到新到容量为8的内存地址中,然后再继续接收新元素并使用。

所以说:在初始化时,如果我们知道map大概有多少元素时,可以初始化时指定,这样可以在一定程度上提升性能(频繁扩容影响性能)

1.7 如何访问map中的元素

通过key访问map中的元素

package main

import "fmt"

func main() {
a := map[string]int{
"steve": 12000,
"jamie": 15000,
}
a["mike"] = 9000
b := "jamie"
fmt.Println("Salary of", b, "is", a[b])
}

执行结果:

1.8 map添加键值对

map中的数据都是成对出现的,map的基本使用示例代码如下:

func main() {
scoreMap := make(map[string]int, 8)
scoreMap["张三"] = 90
scoreMap["小明"] = 100
fmt.Println(scoreMap)
fmt.Println(scoreMap["小明"])
fmt.Printf("type of a:%T\n", scoreMap)
}

输出:

map[小明:100 张三:90]
100
type of a:map[string]int

map也支持在声明的时候填充元素,例如:

func main() {
userInfo := map[string]string{
"username": "哈登",
"password": "123456",
}
fmt.Println(userInfo) //
}

1.9 判断某个键(key)是否存在

Go语言中有个判断map中键是否存在的特殊写法,

格式如下:

value, ok := map[key]

相当于做一个白名单,去判断map中指定key是否存在

注意:ok仅仅是个变量,可以随便命名

举个例子:

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("查无此人")
}
}

实例2:

用户白名单

package main

import (
"fmt"
) var whiteUser map[int]bool = map[int]bool{
32323: true,
32011: true,
10222: true,
} func isWhiteUser(userId int) bool {
_, ok := whiteUser[userId] //这里不需要返回value,所以直接_忽略
return ok
} func main() {
userId := 100021
if isWhiteUser(userId) {
fmt.Printf("is white user:%v\n", userId)
} else {
fmt.Printf("is normal user:%v\n", userId)
}
}

执行结果:

1.10 map的遍历

Go语言中使用for range遍历map。

range返回key value并赋值给变量,结合数组、切片遍历也是for range,其实数组及切片就是个特殊map。

func main() {
scoreMap := make(map[string]int)
scoreMap["张三"] = 90
scoreMap["小明"] = 100
scoreMap["娜扎"] = 60
for k, v := range scoreMap {
fmt.Println(k, v)
}
}

但我们只想遍历key的时候,可以按下面的写法:

package main

import "fmt"

func main() {
scoreMap := map[string]int{
"张三": 90,
"小明": 100,
"张飞": 80,
}
for k := range scoreMap {
fmt.Println(k)
}
}

注意: 遍历map时输出的元素顺序与填充顺序无关。

1.11 使用delete()函数删除键值对

使用delete()内建函数从map中删除一组键值对,delete()函数的格式如下:

delete(map, key)

其中, map:表示要删除键值对的map ,key:表示要删除的键值对的键

示例代码如下:

func main(){
scoreMap := make(map[string]int)
scoreMap["张三"] = 90
scoreMap["小明"] = 100
scoreMap["娜扎"] = 60
delete(scoreMap, "小明")//将小明:100从map中删除
for k,v := range scoreMap{
fmt.Println(k, v)
}
}

实例2:

package main

import (
"fmt"
) func main() {
a := map[string]int{
"steve": 12000,
"jamie": 15000,
}
a["mike"] = 9000
fmt.Println("map before deletion", a)
delete(a, "steve")
fmt.Println("map after deletion", a)
}

执行结果:

1.12 map的长度

借助len函数

代码示例:

package main

import (
"fmt"
) func main() {
a := map[string]int{
"steve": 12000,
"jamie": 15000,
}
a["mike"] = 9000
fmt.Println("length is", len(a))
}

执行结果:

1.13 按照指定顺序遍历map(扩展)

func main() {
rand.Seed(time.Now().UnixNano()) //初始化随机数种子 var 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
var 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])
}
}

1.14 map是引用类型

通过下面这个例子验证map是引用类型

代码示例如下:

package main

import (
"fmt"
) func main() {
a := map[string]int{
"steve": 12000,
"jamie": 15000,
}
a["mike"] = 9000
fmt.Println("origin map", a)
b := a
b["mike"] = 18000
fmt.Println("a map changed", a)
}

执行结果如下:

解释:

可以发现a为map,将a赋值给b,b也是map,map b做了修改,此时打印a,map a也发生了变化,这证明map是引用类型

1.7 元素为map类型的切片

下面的代码演示了切片中的元素为map类型时的操作:

实例1:

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]string, 10)
mapSlice[0]["name"] = "哈登"
mapSlice[0]["password"] = "123456"
mapSlice[0]["address"] = "休斯顿"
for index, value := range mapSlice {
fmt.Printf("index:%d value:%v\n", index, value)
}
}

实例2:

package main

import (
"fmt"
) func main() {
var mapSlice []map[string]int
mapSlice = make([]map[string]int, 5) //此时是一个未初始化的map类型切片,所以需要先将切片初始化
fmt.Println("before map init")
for index, value := range mapSlice {
fmt.Printf("index:%d value:%v\n", index, value)
}
fmt.Println()
mapSlice[0] = make(map[string]int, 10) //要使用map,需要将map进行初始化。
mapSlice[0]["a"] = 1000
mapSlice[0]["b"] = 2000
mapSlice[0]["c"] = 3000
mapSlice[0]["d"] = 4000
mapSlice[0]["e"] = 5000
fmt.Println("after map init")
for index, value := range mapSlice {
fmt.Printf("index:%d value:%v\n", index, value)
}
}

执行结果:

1.8 值为切片类型的map

下面的代码演示了map中值为切片类型的操作:

package main

import "fmt"

func main() {
var 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)
}

执行结果:

2.5 Go语言基础之map的更多相关文章

  1. Go语言基础之map

    Go语言基础之map Go语言中提供的映射关系容器为map,其内部使用散列表(hash)实现. map map是一种无序的基于key-value的数据结构,Go语言中的map是引用类型,必须初始化才能 ...

  2. GO学习-(11) Go语言基础之map

    Go语言基础之map Go语言中提供的映射关系容器为map,其内部使用散列表(hash)实现. map map是一种无序的基于key-value的数据结构,Go语言中的map是引用类型,必须初始化才能 ...

  3. go语言基础之map赋值、遍历、删除 、做函数参数

    1.map赋值 示例: package main //必须有个main包 import "fmt" func main() { m1 := map[int]string{1: &q ...

  4. go语言基础之map介绍和使用

    1.map介绍 Go语言中的map(映射.字典)是一种内置的数据结构,它是一个无序的key—value对的集合,比如以身份证号作为唯一键来标识一个人的信息. 2.map示例 map格式为: map[k ...

  5. 【GoLang】GO语言系列--002.GO语言基础

    002.GO语言基础 1 参考资料 1.1 http://www.cnblogs.com/vimsk/archive/2012/11/03/2736179.html 1.2 https://githu ...

  6. (cljs/run-at (->JSVM :browser) "语言基础")

    前言  两年多前知道cljs的存在时十分兴奋,但因为工作中根本用不上,国内也没有专门的职位于是搁置了对其的探索.而近一两年来又刮起了函数式编程的风潮,恰逢有幸主理新项目的前端架构,于是引入Ramda. ...

  7. Go语言基础(二)

    Go语言基础(二) 跟着上篇,继续看Go基础 一.变量作用域 与C类似,有全局变量.局部变量.形参之分 package main import "fmt" // 全局变量 var ...

  8. Go语言基础之反射

    Go语言基础之反射 本文介绍了Go语言反射的意义和基本使用. 变量的内在机制 Go语言中的变量是分为两部分的: 类型信息:预先定义好的元信息. 值信息:程序运行过程中可动态变化的. 反射介绍 反射是指 ...

  9. Go语言基础之接口

    Go语言基础之接口 接口(interface)定义了一个对象的行为规范,只定义规范不实现,由具体的对象来实现规范的细节. 接口 接口介绍 在Go语言中接口(interface)是一种类型,一种抽象的类 ...

随机推荐

  1. swoole深入学习 8. 协程 转

    版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/yangyi2083334/article/ ...

  2. 【简易DFS/BFS+标记搜索次序的数组】zznu-2025 : 简单环路

    2025 : 简单环路 时间限制:1 Sec 内存限制:128 MiB提交:145 答案正确:41 提交 状态 编辑 讨论区 题目描述 有一个N x M 大小的地图,地图中的每个单元包含一个大写字母. ...

  3. 解决使用JPA时对象在set属性时更新了数据库问题

    https://www.jianshu.com/p/1100814ff54f 之前也遇到过一次这个问题,打印的日志中也可以看见update相关的sql语句,但当时不知道为什么会去自动更新,就用了别的方 ...

  4. [唐胡璐]MongoDB - 在Win7下环境搭建

    做Selenium一直都是用的Excel来管理数据驱动的数据,现在想用MongoDB来管理,所以对MongoDB做一个简单的了解应用: Include the below items:1. what ...

  5. Java锁机制总结

    锁是用于控制多线程对共享资源的访问. Java中的锁可以分为内置锁与显式锁Lock.其中内置锁指synchronized关键字. Synchronized synchronized可以修饰方法或代码块 ...

  6. 推荐一款在IntelliJ IDEA中使用微信/QQ的插件

    SmartIM SmartIM4IntelliJ 是一个 IntelliJ IDEA 上的 SmartIM(原 SmartQQ)插件,可以在 IDEA 中使用 QQ 或微信聊天. 功能 收发文本消息 ...

  7. 使用Eclipse进行远程调试(转)

    做开发好多年了,Debug大家肯定都不陌生,绝对称得上是家常便饭了.博主虽不敢妄下断言,但是这里也猜一下,肯定有很多人都没有使用过Remote Debug(远程调试).说来惭愧,博主也是工作了3年才用 ...

  8. 17、生命周期-BeanPostProcessor在Spring底层的使用

    17.生命周期-BeanPostProcessor在Spring底层的使用 bean赋值.注入其他组件.@Autowired注解.生命周期注解.@Async --都是 BeanPostProcesso ...

  9. 第二章 C#语法快速热身

    C#语法快速热身 语法 if(条件表达式){ 代码块 } 语法 if(条件表达式){ 代码块 }else{ 代码块2 } 语法 if(条件表达式1){ 代码块1 if(条件表达式1)){ }else{ ...

  10. fckeditor实现ctrl+v粘贴word图片并上传

    tinymce是很优秀的一款富文本编辑器,可以去官网下载.https://www.tiny.cloud 这里分享的是它官网的一个收费插件powerpaste的旧版本源码,但也不影响功能使用. http ...