第 9 章 - GO语言 映射(Map)

在Go语言中,map 是一种无序的键值对集合。每个键只能出现一次,不同的键可以映射到相同的值。下面将详细介绍 map 的定义、使用以及操作和遍历方法。

Map的定义和使用

定义一个Map

要定义一个 map,需要指定键和值的数据类型。使用 make 函数来创建一个新的 map,或者使用字面量语法来初始化一个带有初始值的 map

// 使用 make 创建一个 map
ages := make(map[string]int)// 使用字面量语法创建并初始化一个 map
ages := map[string]int{"Alice": 30,"Bob":   25,
}
向Map中添加或更新元素

map 中添加元素或更新已存在的键对应的值,可以直接通过键来赋值。

ages["Charlie"] = 35 // 添加新元素
ages["Alice"] = 31   // 更新已有元素
访问Map中的元素

可以通过键来访问 map 中的值。如果键不存在于 map 中,那么返回值将是该值类型的零值。

age := ages["Alice"] // 获取 Alice 的年龄

为了检查键是否存在于 map 中,可以使用如下方式:

age, exists := ages["Alice"]
if exists {fmt.Println("Alice's age is", age)
} else {fmt.Println("Alice's age not found")
}

Map的操作

删除Map中的元素

使用 delete 函数可以从 map 中删除一个键值对。

delete(ages, "Bob") // 删除 Bob 的记录
检查Map是否为空

可以通过比较 map 的长度是否为0来判断 map 是否为空。

if len(ages) == 0 {fmt.Println("The map is empty.")
}

Map的遍历

使用for-range循环遍历Map

可以使用 for-range 循环来遍历 map 中的所有键值对。

for key, value := range ages {fmt.Printf("%s: %d\n", key, value)
}

如果只需要遍历键或值,可以使用下划线 _ 忽略不需要的部分。

for key := range ages {fmt.Println("Key:", key)
}for _, value := range ages {fmt.Println("Value:", value)
}

注意事项

  • map 是引用类型,当一个 map 被赋值给另一个变量时,实际上复制的是指向底层数据结构的指针。因此,对其中一个 map 的修改会影响到另一个。
  • map 在多 goroutine 并发写入时需要同步控制,否则会导致运行时错误。可以使用 sync.Map 类型来安全地处理并发读写操作。

以上是Go语言中 map 的基本介绍和使用方法。希望这些信息对你有所帮助!

接下来我将继续补充一些关于 Go 语言中 map 的高级用法和一些最佳实践。

高级用法

初始化空的Map

如果你知道 map 将会存储大量的数据,可以预先指定其容量以优化性能。

// 预分配空间
ages := make(map[string]int, 100)
嵌套Map

map 可以嵌套其他 map,这在需要表示复杂数据结构时非常有用。

// 创建一个嵌套 map
var people = map[string]map[string]int{}// 添加嵌套 map
people["Alice"] = map[string]int{"Age": 30, "Height": 170}
people["Bob"] = map[string]int{"Age": 25, "Height": 180}
使用 sync.Map 进行并发安全操作

当多个 goroutines 需要同时读写同一个 map 时,使用标准库中的 sync.Map 可以避免竞态条件。

import "sync"var m sync.Mapm.Store("Alice", 30) // 插入或更新
value, _ := m.Load("Alice") // 读取
m.Delete("Alice") // 删除

最佳实践

键的类型选择
  • 键应该是不可变的,通常使用基础类型如 stringint 或者指针作为键。
  • 不推荐使用复杂类型(如结构体)作为键,除非它们实现了 Comparable 接口。
容量预分配
  • 对于已知大小的 map,建议在创建时预分配足够的容量,以减少内存重新分配的次数,提高性能。
错误处理
  • 当从 map 中获取值时,总是检查返回的第二个布尔值,以确认键是否存在。
避免不必要的拷贝
  • 如果 map 包含大型值类型,考虑使用指针作为值,以避免不必要的内存拷贝。
并发安全
  • 在并发环境中使用 map 时,确保使用适当的同步机制,如互斥锁 sync.Mutex 或者 sync.RWMutex,或者直接使用 sync.Map

示例代码

这里是一个简单的示例,展示了如何使用 sync.Map 来实现线程安全的计数器:

package mainimport ("fmt""sync"
)func main() {var counter sync.Map// 增加计数increment := func(key string) {if val, ok := counter.Load(key); ok {counter.Store(key, val.(int)+1)} else {counter.Store(key, 1)}}// 并发增加计数var wg sync.WaitGroupfor i := 0; i < 100; i++ {wg.Add(1)go func(i int) {defer wg.Done()increment(fmt.Sprintf("counter%d", i%10))}(i)}wg.Wait()// 输出结果counter.Range(func(k, v interface{}) bool {fmt.Printf("%v: %v\n", k, v)return true})
}

这个程序创建了一个线程安全的计数器,并在多个 goroutines 中并发地增加了计数。最后,它遍历 sync.Map 并打印所有计数器的值。

希望这些补充内容能够帮助你更好地理解和使用 Go 语言中的 map

我将继续补充一些关于 Go 语言中 map 的高级特性和最佳实践,包括一些常见的应用场景和技巧。

高级特性

使用 nil Map

在 Go 中,nil map 和空 map 是不同的。尝试对 nil map 进行读写操作会导致运行时错误。因此,在使用 map 之前,确保它已经被初始化。

var m map[string]int // m 是 nil map// 下面这行代码会导致运行时错误
m["key"] = 1// 正确的做法是先初始化
m = make(map[string]int)
m["key"] = 1
动态键类型

虽然 map 的键类型必须是可比较的,但在某些情况下,你可能需要动态地处理不同类型的键。可以使用接口类型 interface{} 作为键,但这通常需要额外的类型断言和检查。

var dynamicMap map[interface{}]int
dynamicMap = make(map[interface{}]int)dynamicMap["stringKey"] = 1
dynamicMap[123] = 2// 读取时需要类型断言
value, ok := dynamicMap["stringKey"].(int)
if ok {fmt.Println(value)
}
使用 map 实现缓存

map 经常用于实现简单的缓存机制。可以结合 time.After 等函数来实现过期功能。

package mainimport ("fmt""time"
)type Cache struct {data map[string]interface{}
}func NewCache() *Cache {return &Cache{data: make(map[string]interface{}),}
}func (c *Cache) Get(key string) (interface{}, bool) {value, exists := c.data[key]return value, exists
}func (c *Cache) Set(key string, value interface{}, ttl time.Duration) {c.data[key] = valuego func() {time.Sleep(ttl)delete(c.data, key)}()
}func main() {cache := NewCache()cache.Set("key1", "value1", 5*time.Second)value, exists := cache.Get("key1")if exists {fmt.Println("Cache hit:", value)} else {fmt.Println("Cache miss")}time.Sleep(6 * time.Second)value, exists = cache.Get("key1")if exists {fmt.Println("Cache hit:", value)} else {fmt.Println("Cache miss")}
}

最佳实践

键的唯一性

确保 map 的键是唯一的,重复的键会导致覆盖前一个值。

ages := map[string]int{"Alice": 30,"Alice": 31, // 这个值会覆盖前面的值
}
键的可比较性

键必须是可比较的类型,如 intstringfloat64、指针等。不可比较的类型(如切片、映射、函数)不能用作键。

// 错误示例
// var invalidMap = map[[2]int]string{} // [2]int 是不可比较的
避免频繁的 map 复制

map 是引用类型,但在传递给函数或赋值给其他变量时,会复制其引用。如果需要复制 map 的内容,可以手动创建一个新的 map 并复制所有键值对。

func copyMap(m map[string]int) map[string]int {newMap := make(map[string]int, len(m))for k, v := range m {newMap[k] = v}return newMap
}original := map[string]int{"Alice": 30, "Bob": 25}
copied := copyMap(original)
使用 map 代替多重嵌套的 if 语句

在某些情况下,使用 map 可以简化代码逻辑,特别是当需要根据某个键执行不同的操作时。

actions := map[string]func(int){"add": func(x int) { fmt.Println("Adding:", x) },"sub": func(x int) { fmt.Println("Subtracting:", x) },
}action := "add"
if f, ok := actions[action]; ok {f(10)
} else {fmt.Println("Unknown action")
}

常见应用场景

配置管理

使用 map 存储配置项,便于动态管理和查找。

config := map[string]string{"server": "localhost","port":   "8080",
}server := config["server"]
port := config["port"]
fmt.Printf("Server: %s, Port: %s\n", server, port)
数据统计

使用 map 进行数据统计,例如统计单词出现的频率。

text := "hello world hello go world"
words := strings.Split(text, " ")wordCount := make(map[string]int)
for _, word := range words {wordCount[word]++
}for word, count := range wordCount {fmt.Printf("%s: %d\n", word, count)
}
路由表

在 Web 框架中,使用 map 存储路由和处理函数的映射关系。

routes := map[string]func(http.ResponseWriter, *http.Request){"/home": homeHandler,"/about": aboutHandler,
}http.HandleFunc("/home", routes["/home"])
http.HandleFunc("/about", routes["/about"])

总结

map 是 Go 语言中非常强大和灵活的数据结构,适用于多种场景。理解其基本用法和高级特性,遵循最佳实践,可以帮助你编写更高效、更可靠的代码。如果有任何具体问题或需要进一步的示例,请随时提问!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/14739.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

np.zeros_like奇怪的bug

import numpy as np aa np.array([[1,2,3],[2,3,3]]) cc np.random.randn(2,3) print(aa) print(cc)bb np.zeros_like(aa) print(bb)for i in range(bb.shape[0]):for j in range(bb.shape[1]):bb[i,j] cc[i,j]print(bb)结果如下 这里发现这个bb的结果是没有赋值的 正确做…

C++(Qt)软件调试---内存泄漏分析工具MTuner (25)

C(Qt)软件调试—内存泄漏分析工具MTuner &#xff08;25&#xff09; 文章目录 C(Qt)软件调试---内存泄漏分析工具MTuner &#xff08;25&#xff09;[toc]1、概述&#x1f41c;2、下载MTuner&#x1fab2;3、使用MTuner分析qt程序内存泄漏&#x1f9a7;4、相关地址&#x1f41…

apk反编译修改教程系列-----apk应用反编译中AndroidManifest.xml详细代码释义解析 包含各种权限 代码含义

在反编译apk应用中。需要增加或者减少有些apk功能或者权限类的修改。其中大多都在于 AndroidManifest.xml文件中。了解AndroidManifest.xml其中每串代码代表的含义对修改apk有着至关重要的作用。 通过博文了解💝💝💝💝 1💝💝💝💝----AndroidManifest.xml中代…

项目功能--运营数据统计

一、需求分析 通过运营数据统计可以展示出体检机构的运营情况&#xff0c;包括会员数据、预约到诊数据、热门套餐等信息。我们要通过一个表格的形式来展示这些运营数据。如下图&#xff1a; 二、代码实现 实现步骤&#xff1a; 步骤一&#xff1a;定义数据模型&#xff0c;通过…

电子制造行业Top5贴片机品牌

在电子制造业的快速发展中&#xff0c;SMT&#xff08;Surface Mount Technology&#xff09;表面贴装技术扮演着至关重要的角色。贴片机作为SMT生产线的核心设备&#xff0c;其性能直接关系到整个生产线的效率和产品质量。 SPEA作为全球领先的自动化测试设备服务商&#xff0…

【maven踩坑】一个坑 junit报错 但真正导致这个的不是junit的原因

目录 事件起因环境和工具操作过程解决办法结束语 事件起因 报错一&#xff1a; Internal Error occurred. org.junit.platform.commons.JUnitException: TestEngine with ID junit-vintage failed to discover tests报错二&#xff1a; Internal Error occurred. org.junit.pl…

拷贝和浅拷贝的区别,以及对于循环引用如何处理深拷贝

深拷贝和浅拷贝的区别&#xff0c;以及对于循环引用如何处理深拷贝 浅拷贝仅拷贝对象的第一层属性值&#xff0c;对于基本数据类型&#xff0c;会复制其值&#xff1b;对于引用数据类型&#xff0c;仅复制引用地址而不复制实际的对象内容。浅拷贝后的新对象与原对象中的引用类…

gitlab与jenkins

一 gitlab代码仓库 1.1 gitlab简介 GitLab 是一个用于仓库管理系统的开源项目&#xff0c;使用 Git 作为代码管理工具&#xff0c;并在此基础上搭建起来的 web 服务。GitLab 具有很多功能&#xff0c;比如代码托管、持续集成和持续部署&#xff08;CI/CD&#xff09;、问题跟踪…

LeetCode 86.分隔链表

题目&#xff1a; 给你一个链表的头节点 head 和一个特定值 x &#xff0c;请你对链表进行分隔&#xff0c;使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。 你应当 保留 两个分区中每个节点的初始相对位置。 思路&#xff1a; 代码&#xff1a; /*** Definiti…

Qt/C++ 开源控件 可折叠的标签管理控件

在 Qt 开发中&#xff0c;许多项目需要处理标签管理功能&#xff0c;例如分类管理、标签筛选等需求。本文将分享如何利用 Qt/C 实现一个具备动态增删标签、展开折叠功能的控件。此控件由 TagWindow 和 TagItemWidget 两个类组成&#xff0c;前者负责整个标签管理窗口的布局与逻…

Jmeter中的监听器(三)

9--断言结果 功能特点 显示断言结果&#xff1a;列出所有断言的结果&#xff0c;包括通过和失败的断言。详细信息&#xff1a;显示每个断言的详细信息&#xff0c;如断言类型、实际结果和期望结果。错误信息&#xff1a;显示断言失败时的错误信息&#xff0c;帮助调试。颜色编…

七牛云上传图片成功,但是无法访问显示{error : document not found}

上传图片成功&#xff0c;但是访问不了的问题&#xff0c;直接把地址放进浏览器显示{error : document not found}&#xff0c;直接访问 DCNF 404是符合预期的&#xff0c;因为还没有去空间复制外链&#xff0c;要访问实际存在的资源才可以的. 配置区域和访问域名 设置没问题了…

虚拟与现实交融,线上元宇宙会议应用场景有哪些?

随着科技的飞速发展&#xff0c;元宇宙技术正逐渐渗透到我们生活的各个领域&#xff0c;为企业会议、学术会议、行业展会以及文化娱乐等带来了前所未有的变革。线上元宇宙会议打破了地域和物理空间的限制&#xff0c;让人们能够在虚拟世界中实现跨时空的交互与合作。本文将深入…

构建高效在线商店:Spring Boot框架应用

1 绪论 1.1 研究背景 当前社会各行业领域竞争压力非常大&#xff0c;随着当前时代的信息化&#xff0c;科学化发展&#xff0c;让社会各行业领域都争相使用新的信息技术&#xff0c;对行业内的各种相关数据进行科学化&#xff0c;规范化管理。这样的大环境让那些止步不前&#…

鸿蒙网络编程系列47-仓颉版UDP客户端

1. UDP通讯简介 本系列的第1篇文章《鸿蒙网络编程系列1-UDP通讯示例》中基于ArkTS语言在API 9的环境下演示了UDP通讯的基础用法&#xff0c;本文将使用仓颉语言在API 12的环境中实现类似的功能。这可能听起来有点不太现实&#xff0c;在ArkTS语言下可以利用kit.NetworkKit下的…

Redis与IO多路复用

1. Redis与IO多路复用概述 1.1 Redis的单线程特性 Redis是一个高性能的键值存储系统&#xff0c;其核心优势之一便是单线程架构。在Redis 6.0之前&#xff0c;其所有网络IO和键值对的读写操作都是由一个主线程顺序串行处理的。这种设计简化了多线程编程中的锁和同步问题&…

HarmonyOS Next 组件或页面之间的所有通信(传参)方法总结

系列文章目录 【鸿蒙】HarmonyOS NEXT开发快速入门教程之ArkTS语法装饰器&#xff08;上&#xff09; 【鸿蒙】HarmonyOS NEXT开发快速入门教程之ArkTS语法装饰器&#xff08;下&#xff09; 【鸿蒙】HarmonyOS NEXT应用开发快速入门教程之布局篇&#xff08;上&#xff09; 【…

API接口:助力汽车管理与安全应用

随着汽车行业的飞速发展&#xff0c;越来越多的汽车管理技术被应用到交通安全和智慧交通系统中。在这一过程中&#xff0c;API接口起到了至关重要的作用。通过API接口&#xff0c;我们可以实现诸如车主身份验核、车辆信息查询等功能&#xff0c;从而为汽车智慧交通发展与安全应…

C哈的刷题计划之输出数字螺旋矩阵(1)

1、盲听C哈说 都说数据结构与算法是编程的核心&#xff0c;它们两个是内功与心法&#x1f600;&#xff0c;其它编程工具只是招式&#xff0c;学会了内功与心法&#xff0c;学习新事物&#xff08;这里特指层出不穷的IT技术&#xff09;就没有那么难了&#xff0c;实际上&#…

AD22Duplicate Net Names Wire问题

在验证的时候发现报了这个错误 我这个原理图都是用自定义的元件 只写在name引脚名字是会报这个错的 但是换成designator引脚标识就不会了 建议是name引脚名字和designator引脚标识都写 写成一样都行&#xff0c;就不会报这个错了&#xff0c;别空着