详解Go语言Map

Map的声明

  1. 使用make()函数定义Map
go 复制代码
map_name := make(map[KeyType]valueType,initialCapacity)

KeyType是键的类型,ValueType是值的类型,initialCapacity是可选参数,用于指定map的初始容量。

  1. 使用map关键字定义Map
go 复制代码
m := map[string]int {
	"apple": 1,
	"banana": 2,
}

Map的基本操作

  • 获取元素
go 复制代码
//获取键值对
v1 := m["apple"]
v2, ok := m["pear"] //如果键不存在, ok的值为false,v2的值为该类型的零值
  • 修改元素
go 复制代码
//修改键值对
m["apple"]=5
  • 获取Map的长度
go 复制代码
//获取Map的长度
len := len(m)
  • 遍历Map
go 复制代码
for k, v := range m {
	fmt.Println("%s, %d\n",k, v)
}
  • 删除元素
go 复制代码
delete(m, "banana")

注意:若该元素不存在于指定map,delete函数也不会报错,也不会有任何影响

Go-Map使用心得及注意事项

1. 不支持并发操作(非线程安全)

当多个线程操作同一个map时,会引发线程并发,并报错fatal error: concurrent map read and map write

如何解决这个问题?

1. sync.Mutex

当存在线程正在对map进行读写的时候,不允许其他线程进入map进行读写,让其在外面等待,直到正在读写的线程操作完毕。

go 复制代码
package main

import (
	"fmt"
	"sync"
)

func main() {
	// 创建一个包含键值对的map
	myMap := make(map[string]int)

	// 创建一个互斥锁
	var mu sync.Mutex

	// 开启多个goroutine并发写入map
	for i := 0; i < 10; i++ {
		go func() {
			// 对map加锁
			mu.Lock()
			defer mu.Unlock()

			// 向map中写入数据
			myMap["key"] = myMap["key"] + 1
		}()
	}

	// 等待所有goroutine执行完成
	for i := 0; i < 10; i++ {
		go func() {
			// 对map加锁
			mu.Lock()
			defer mu.Unlock()

			// 从map中读取数据
			fmt.Println(myMap["key"])
		}()
	}

	// 等待一段时间,确保所有goroutine执行完成
	time.Sleep(time.Second)
}

线程的每次对map进行读写前对map进行加锁,当操作完成后解锁,这样就避免了并发问题。

缺点:读写性能低。每次进行读写操作都需要对整个map进行加锁,锁的颗粒度相当大。如果出现两个线程操作的map的不同区域,互不影响,完全可以将锁的颗粒度减少为线程当前需要操作的区域,这样就能大大提高读写效率。

2. sync.Map

在 Golang 中,sync.Map 是一个并发安全的 Map 实现,可以在多个 Goroutine 中安全地读写 Map。

go 复制代码
package main

import (
	"fmt"
	"sync"
)

func main() {
	m := sync.Map{}

	var wg sync.WaitGroup
	wg.Add(2)

	go func() {
		defer wg.Done()
		m.Store("key1", "value1")
	}()

	go func() {
		defer wg.Done()
		value, _ := m.Load("key1")
		fmt.Printf("Value of key1: %v\n", value)
	}()

	wg.Wait()
}

缺点:

  1. 使用较为复杂。value类型是一个interface{},需要断言使用。
  2. 适用读多写少的场景。读写分离的实现方式,在读多写少的场景性能优越,但是写多的场景中,读操作命中率低,需要不断同步读写map,性能降低。

2. map的初始化

map的使用需要先初始化。

如果只是定义了map而没有进行初始化,对map进行写操作会报错,因为map此时为nil。

  • 写操作:报错panic: assignment to entry in nil map

但是可以进行读操作,获取某一个值,返回的是value类型的默认值。

3. key的类型

go中,map的key不支持func、slice、map这三种类型。

4. map本身为一个引用数据类型(指针)

map在程序中是一个指针类型的,传参过程中,实参和形参指的是同一个实体。

相关推荐
代码N年归来仍是新手村成员4 小时前
【Java转Go】即时通信系统代码分析(一)基础Server 构建
java·开发语言·golang
DICOM医学影像10 小时前
2. go语言从零实现以太坊客户端-查询区块链账户余额
开发语言·golang·区块链·以太坊·web3.0·hardhat
西京刀客12 小时前
golang路由与框架选型(对比原生net/http、httprouter、Gin)
http·golang·gin
Mr -老鬼12 小时前
Rust与Go:从学习到实战的全方位对比
学习·golang·rust
DICOM医学影像15 小时前
1. go语言从零实现以太坊客户端-JSON-RPC
golang·区块链·solidity·以太坊·web3.0·json-rpc·erc20
PXM的算法星球15 小时前
用 semaphore 限制 Go 项目单机并发数的一次流量控制优化实践
开发语言·后端·golang
ZNineSun16 小时前
GORM:Go的ORM 框架
golang·orm·gorm·crud
源代码•宸16 小时前
Golang语法进阶(定时器)
开发语言·经验分享·后端·算法·golang·timer·ticker
a程序小傲16 小时前
得物Java面试被问:边缘计算的数据同步和计算卸载
java·开发语言·数据库·后端·面试·golang·边缘计算
nbsaas-boot1 天前
Go vs Java 的三阶段切换路线图
java·开发语言·golang