Golang Map简介

server/2024/10/20 12:06:42/

Go Map

Map 简介

在Go语言中提供了map数据结构来存储键值对数据。map的数据类型为map[K]V,其中K为键的类型,V为值的类型。map的键类型必须支持==操作符,用来比较两个键是否相等。Go语言提供了4种内置的map操作: lendeletecomparisonassign

Map 定义


map_var := make(map[K]V) // 用make函数创建一个空的map,其中K和V分别为键和值的类型map_var[key] = value // 向map中添加一个键值对value := map_var[key] // 获取指定键的值delete(map_var, key) // 从map中删除指定的键及其对应的值

Map Iteration

Go语言提供了两个方法来遍历map中的所有键值对,分别是range方法和Len()方法。


// 使用range循环遍历map中的所有键值对for key, value := range map_var {// TODO ...}// 计算map中的元素数量if len(map_var) > 0 {// TODO ...}

Map 的线程安全

在Go语言中,map是非线程安全的,在多线程并发访问时可能导致程序报错。当map被多个协程同时访问时,我们需要使用sync包中的sync.Mutex来确保操作的原子性和并发安全。


import "sync"type SafeMap struct {mu sync.Mutexm map[string]int}func (sm *SafeMap) Get(key string) int {sm.mu.Lock()defer sm.mu.Unlock()return sm.m[key]}func (sm *SafeMap) Set(key string, value int) {sm.mu.Lock()defer sm.mu.Unlock()sm.m[key] = value}func (sm *SafeMap) Delete(key string) {sm.mu.Lock()defer sm.mu.Unlock()delete(sm.m, key)}

map 底层原理

Go语言的map在设计上是一种哈希表的数据结构。它利用哈希函数将键映射到不同的存储空间,从而实现高效的查找和插入操作。

哈希函数

哈希函数将字符串映射到一个整数上,这称为哈希值。不同的字符串可能会有相同的哈希值,但相同的字符串必定具有相同的哈希值。哈希函数需要满足两点:

  • 哈希函数的计算结果必须是非负整数,因为负数无法在数组中表示。

  • 两个不同字符串的哈希值尽量不要相等,这样可以避免在查找时产生冲突。

在Go语言中,字符串的哈希函数采用的是FNV-1哈希算法,算法代码如下:


const (offset64 = 14695981039346656037prime64 = 1099511628211)func stringHash(s string) uint64 {h := uint64(offset64)for i := 0; i < len(s); i++ {h ^= uint64(s[i])h *= prime64}return h}

哈希冲突

在哈希表中,哈希值相同的多个字符串可能会存储在同一个位置上,这种现象叫做哈希冲突。哈希冲突处理策略有开放寻址法、再哈希法和链地址法。

  • 开放寻址法:将发生冲突的条目逐个检索新的空棑直到找到一个空位置来存储当前键值对

  • 再哈希法:对于发生冲突的键,用另一个不同的哈希函数计算地址

  • 链地址法:对于发生冲突的键,将其存储在一个链表中

Go语言使用链地址法处理哈希冲突。对于每个存储单元,map结构体中还维护了一个[]keyValue类型的链表。


type hmap struct {count int // 映射中的键值对数量flags uint8 // 控制哈希表的一些属性B uint8 // 用于计算哈希地址的初始大小noverflow uint16 // 链表上的溢出桶的数量}

Growing

在Go语言中,动态数组会自动地为map分配更多的空间。Growing过程涉及到将原始的数组重新复制到一个更大的数组中,其中原数组的元素需要重新计算其在新数组中的位置,而新数组的元素则需要将其键值对填充到相应的位置。Growing的过程比较复杂,可以由函数hashGrow()来控制。


// hashGrow() 将map的数组的大小翻倍,并处理哈希冲突。func hashGrow(h *hmap) {// ...buf := make([]keyValue, newCap)//...for i := uintptr(0); i < cap; i++ {// ...evacuate(h, &h.oldbuckets[i], &buf)// ...}// ...}// evacuate() 将一个bucket中的键值对重新映射到新的数组中func evacuate(h *hmap, oldbuck *bucket, newbuck *[]keyValue) {// ...}

map扩容

双倍扩容

Go语言中的哈希表在map的数组容量达到一定程度时,就会自动进行扩容。扩容的依据是当前已存储的元素数量和数组的长度之间的比值:

  • 当map的已存储元素数量小于map数组长度的一半时,元素的数量未达到哈希表效率的最大值,无需扩容;

  • 当map已存储的元素数量大于等于map数组长度的一半时,哈希表的查找效率已达到最大值,所以需要扩容。

Go语言的map会优先选择数组大小为原数组大小的2倍,以确保map在存储过程中有足够的空间存放新的元素。当元素数量达到85%时,Go语言就会再次对数组进行扩容,此时数组长度翻倍,以保证数组长度和元素数量的比例始终维持在0.75左右,以平衡效率和空间占用。

Growing过程

当映射中的元素数量超过85%时,Go语言就会触发map的扩容过程。在扩容的过程中,map会将原有的元素复制到新的数组中,并将新数组的初始大小设置为原数组的2倍。对于发生哈希冲突的元素,需要在新的数组中重新计算哈希地址。

避免溢出

当数组中元素的数量超过0x7fffffff(2^31-1,即int类型的最大值)时,就会发生溢出,此时数组的大小将无法达到原数组的2倍。所以Go语言会在初始创建map时,为其初始化一个较小的数组,并设置map的B值,以便在元素数量超过限制时再次进行扩容。当map中元素的数量超过阈值时,会再次翻倍,直到数组大小小于0x7fffffff为止。

代码分析

hashmap.go包含在Go语言源码中的src/container/map.go文件中。其中map结构体的定义和Growing实现都在runtime包中,在src/runtime/map.go文件中。

附录

  • 为什么哈希表的容量要设置为2的n次幂?为什么不是其他数字?

  • Go语言中的map是如何进行线程安全的?原理是什么?

  • map的数据结构是怎样的?如何实现键值对的查找、添加、删除操作?

  • 如何实现Growing过程?

  • 为什么map的扩容条件是85%,而不是100%?

  • 在go语言中如何创建map?

  • 为什么哈希冲突处理策略有开放寻址法、再哈希法和链地址法?

  • 如果存在冲突,键值对是如何存储在数组中的?

  • 为什么Growing过程中会创建一个较大的临时数组,而不是直接在原数组上扩展空间?

  • 如何实现map的迭代?

总结

本节我们学习了Go语言中的map数据类型,使用方法以及map的数据安全问题。


http://www.ppmy.cn/server/133330.html

相关文章

关于C语言——对一个数据定义的两种属性

对一个数据的定义&#xff0c;需要去定义它的两种属性:数据类型和存储类型。 对于数据类型主要有: intcharlongfloatdouble 对于存储类型有这四种: auto static register extern 平时使用的时候一般不标明存储类型&#xff0c;而存储类型主动是为auto&#xff0c;自动变…

基于x86_64汇编语言简单教程1: 环境预备与尝试

目录 前言 环境配置 基本硬件与操作系统要求 WSL VSCode基本配置(For Windows) 安装基本的依赖 为您的VSCode安装插件&#xff1a; 学习要求 入门 先试试味道 前言 笔者最近正在梭哈使用NASM汇编器的x86 32位汇编&#xff0c;笔者这里记录一下一个晚上的成果。 环境…

Java爬虫:获取商品评论数据的高效工具

在电子商务的激烈竞争中&#xff0c;商品评论作为消费者购买决策的重要参考&#xff0c;对于商家来说具有极高的价值。它不仅能够帮助商家了解消费者的需求和反馈&#xff0c;还能作为改进产品和服务的依据。Java爬虫技术&#xff0c;以其稳健性和高效性&#xff0c;成为了获取…

探索 Python Web 开发:从框架到爬虫

Python 是 Web 开发中广泛使用的编程语言&#xff0c;因其简单、灵活和强大的生态系统&#xff0c;适合构建各种类型的 Web 应用和 API。在本篇博客中&#xff0c;我们将讨论 Web 开发的几个重要主题&#xff0c;包括 Flask 和 Django 框架、API 开发、HTTP 请求处理以及网页爬…

Python支持向量机(SVM)算法:面向对象的实现与案例详解

目录 Python支持向量机&#xff08;SVM&#xff09;算法&#xff1a;面向对象的实现与案例详解引言一、支持向量机算法概述1.1 支持向量机的基本思想1.2 SVM的分类问题1.3 SVM的优化目标 二、面向对象的SVM实现2.1 类的设计2.2 Python代码实现2.3 代码详解 三、案例分析3.1 案例…

计算机毕业设计Spark+大模型高考分数线预测 知识图谱高考志愿推荐系统 高考数据分析可视化 高考大数据 大数据毕业设计

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 使用vuespringboot前后端分…

985研一学习日记 - 2024.10.16

一个人内耗&#xff0c;说明他活在过去&#xff1b;一个人焦虑&#xff0c;说明他活在未来。只有当一个人平静时&#xff0c;他才活在现在。 日常 1、起床6:00√ 2、健身1个多小时 今天练了二头和背部&#xff0c;明天练胸和三头 3、LeetCode刷了3题 旋转图像&#xff1a…

CollageController

目录 1、 CollageController 1.1、 保存领料主页面 1.1.1、 //审核人 1.1.2、 //审核时间 1.1.3、 //需要删除的ID集合 1.1.4、 //库存表 1.1.5、 //查询原来明细信息 1.1.6、 //修改配件表数量 1.1.7、 //修改配件表数量 1.1.8、 //查询原来明细信息 1.1…