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. MySQL无法启动问题解决Warning: World-writable config file ‘/etc/my.cnf’ is ignored

    今天重启一台内网服务器,发现mysql无法正常重启,执行systemctl start mysql,报错如下 Starting LSB: start and stop MySQL... Dec 11 ...

  2. process exporter 配置项解释

    process exporter在prometheus中用于监控进程,通过process exporter,可从宏观角度监控应用的运行状态(譬如监控redis.mysql的进程资源等) 配置文件样例如 ...

  3. Orion (system-on-a-chip)

      From Wikipedia: Orion is a system-on-a-chip manufactured by Marvell Technology Group and used in n ...

  4. 【数据库】通过触发器实现审计日志记录-demo篇

      触发器实现审计日志记录(记录增.删.改) #创建测试表 CREATE TABLE COMPANY( ID INT PRIMARY KEY NOT NULL, NAME TEXT NOT NULL, ...

  5. 转: Javascript收藏

     搜集的一些javascript小技巧! 事件源对象  event.srcElement.tagName event.srcElement.type 捕获释放 event.srcElement.set ...

  6. Java 基础 面向对象: 接口(interface )概念 以及接口之练习3 -定义一个接口用来实现两个对象的比较并 判断instanceof是否为同类

    接口(interface )概念概念笔记 及测试代码: /** 面向对象: 接口(interface ) 是与类并行的一个概念: * 1.接口可以看成一个特殊的抽象类,它是常量与抽象方法的一个集合 * ...

  7. 理解 shared_ptr实现copy-on-write(COW)

    看muduo库某个生产者消费者的地方,利用shared_ptr有效减少了锁的范围及无用的拷贝,下面来看一看 // reader 消费者, shared_ptr<map<string,int ...

  8. [唐胡璐]Selenium技巧- Prop.Properties配置测试应用的环境和其他配置项

     prop.propertiesfile contains important info that needs to be changed before the test is run, such a ...

  9. 自己实现sizeof+大小端测试

    #define my_sizeof(type) ((char *)(&type+1)-(char*)(&type)) 同时大小端测试 如下 #include <stdio.h&g ...

  10. js实现填写身份证号、手机号、准考证号等信息自动空格的效果

    咱们做网站的,用户体验那是相当重要的,比如12306抢票需要填写身份证,如果不空格,密密麻麻的给我一种很压抑的感觉,而且也不容易核对信息是否填写正确,所以我就写了一个利用Js实现填写身份证号.手机号. ...