”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > Go 中的地图

Go 中的地图

发布于2024-08-24
浏览:167

Maps in Go

简介

Go 合并了一个本地类型,它实现了称为 map 的哈希表。它是一种数据类型,由唯一键的集合和每个键的值的集合组成。
例如,它可以与其他语言中的字典进行比较,字典存储键值对。这些值是使用键访问的,就像我们在上一篇文章中看到的数组和切片一样。
索引不限于数组或切片中的数字,并且元素没有排序,因此如果我们打印地图,如果我们不执行任何操作来覆盖其打印并强制执行所需的顺序,它将返回随机顺序。

地图声明和初始化

要声明一个映射,它是用map[key]value完成的,其中key将是我们想要的键的类型(它必须是可比较的类型https://go.dev/ref/spec#Comparison_operators ) 和 value 将是我们希望映射存储在每个键中的类型,无论它是什么类型,从 int 到结构,或另一个映射,无论我们想要什么。

与切片一样,映射是引用类型,这意味着映射的零值将为 nil。
发生这种情况是因为它下面有一个存储键和值的哈希表,它们只是它们的一个信封和抽象。

如果我们将其声明为:

var m map[int]int

其值为零。

如果我们希望它的值为零,我们可以使用声明:

m := map[int]int{}

我们甚至可以使用 make 函数像切片一样初始化它。

m := make(map[string]string)

这样做将使用适当的内存池初始化哈希映射,从而返回指向该数据结构的映射。

从地图中添加和读取值

向映射添加值是通过使用大括号 [] 和大括号来完成的,就像数组或切片一样。在此示例中,我们将创建一个映射,其中键为字符串,值为整数,以存储姓名和年龄。

ages := make(map[string]int)

ages["John"] = 33
ages["Charly"] = 27
ages["Jenny"] = 45
ages["Lisa"] = 19

如果我们想在声明映射时向其中添加值,我们可以使用简短声明并在同一步骤中完成所有操作:

ages := map[string]int{"John": 33, "Charly": 27, "Jenny": 45, "Lisa": 19}

要读取值,我们只需指示映射的键,它将返回该值。例如,要找出丽莎的年龄,我们可以这样做:

fmt.Println(ages["Lisa"]) // 19

如果我们尝试访问不存在的键,则获得的值将是该类型的零值,在本例中它将是“”,因为它是一个字符串。

为了检查映射中是否存在某个元素,我们可以检查该类型是否为默认类型,但这不是很可靠,因为它可能存在,但其值为空字符串,或者在 int 的情况下为 0 ,它将与其零值匹配,因此 Go 可以帮助我们完成以下操作:

val, ok := ages["Randy"]

如果我们将映射等于两个值,第一个将是通过键访问的该元素的值,在本例中为“Randy”,它不存在,第二个将是一个布尔值,它将指示它是否存在存在与否。

如果我们对值不感兴趣,只是想检查某个键是否存在,我们可以使用 _ 来忽略该值,如下所示:

_, ok := ages["Randy"]

与数组和切片一样,我们可以使用 len 函数来找出映射中有多少个元素。

fmt.Println(len(ages)) // 4

如果我们想要修改一个值,就像使用一个键访问该值并将其与另一个值匹配一样简单,它就会被修改。

如果我们声明第二个映射指向第一个映射,如果我们修改第二个映射的值,因为它是引用类型,我们将修改第一个映射的值,因为两者在下面共享相同的哈希表。

ages := map[string]int{"John": 33, "Charly": 27, "Jenny": 45, "Lisa": 19}
agesNew := ages
agesNew["Bryan"] = 77
fmt.Println(agesNew) // map[Bryan:77 Charly:27 Jenny:45 John:33 Lisa:19]
fmt.Println(ages) // map[Bryan:77 Charly:27 Jenny:45 John:33 Lisa:19]

从地图中删除值

为了从map中删除元素,Go为我们提供了一个删除函数,其签名如下:delete(m map[Type]Type1, key Type),它接收一个map和要删除的key。
在前面的例子中,如果我们想消除“Lisa”,我们会这样做:

delete(ages, "Lisa")

循环遍历地图

如果我们想要遍历地图的内容,我们可以使用 for 和我们在数组和切片的帖子中已经看到的范围变化来完成。
这样,第一个元素将是索引,因此是键,第二个元素是值。

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

// Output:
// Jenny: 45
// Lisa: 19
// John: 33
// Charly: 27

与数组和切片一样,如果我们只对值感兴趣,而不对键感兴趣,我们可以使用 _.
省略它

for _, value := range ages {
    fmt.Println(value)
}

// Output:
// 19
// 33
// 27
// 45

如果我们感兴趣的只是键,我们可以将范围分配给单个变量来获取它:

for key := range ages {
    fmt.Println(key)
}

// Output:
// John
// Charly
// Jenny
// Lisa

对地图进行排序

正如我在简介中提到的,Map 中的信息是没有顺序的,因此在循环遍历它时我们无法指定它遵循的顺序,Go 也不能保证执行之间的顺序是相同的。
正如我们在数组和切片中看到的那样,标准库中有一个排序包可以帮助我们对元素进行排序:https://pkg.go.dev/sort

按照我们的年龄示例并使用排序,我们可以在遍历地图之前对地图的键进行排序,从而保证它将按顺序访问。

ages := map[string]int{"John": 33, "Charly": 27, "Jenny": 45, "Lisa": 19}
keys := make([]string, 0, len(ages))
for k := range ages {
    keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
    fmt.Println(k, ages[k])
}

// Output:
// Charly 27
// Jenny 45
// John 33
// Lisa 19

我们用之前看到的简短声明来声明我们的年龄图。
我们创建一个字符串切片来存储键,并使用长度为 0 的 make 方法,因为我们目前没有任何键,但我们确实使用 len 方法保留了映射长度的容量。
我们遍历年龄映射以保留其键并将它们添加到创建的切片中。
我们使用 sort.Strings 函数按字母顺序对键进行排序。
我们检查已经订购的钥匙片,并使用有问题的钥匙访问地图。
这样我们就可以有序地访问地图,并且可以执行程序所需的逻辑。

并发问题

使用地图时需要记住的一点是它们同时使用是不安全的。如果这些是并发读取,无论是访问一个值还是通过带有范围的 for ,多个 goroutine 同时访问它都没有问题。
有问题的情况是,例如,当您想要通过添加或删除元素来同时更新映射的值,同时从另一侧读取它时。
为了解决这种情况,有几种可能的解决方案,我不会详细介绍,我只会简单地提及并让您选择更深入地研究它们。

如果我们使用标准库中的sync包:https://pkg.go.dev/sync,我们可以控制不同goroutines之间的同步。
一个可能的用途是 RWMutex 类型,它允许我们锁定和解锁对类型的读取和写入。因此,如果我们有一个包含sync.RWMutex和映射的类型,我们可以控制何时可以访问它。
在同一个同步包中研究的另一个有趣的类型是 Map,它已经为我们提供了一系列可以帮助我们使用地图的函数,但最终我们将无法像之前的解决方案一样在本地使用它。
根据我们正在实现的用例,其中一个对我们更有用,并且没有一个比另一个更好,这始终取决于我们的需要。

我希望我在这篇文章中试图解释的一切都已经很清楚了,如果有任何部分还没有完全清楚,或者有一些我没有涵盖但您希望我做的部分,请离开您可以在这里或通过我的社交网络对我的个人资料发表评论,我很乐意回复。

版本声明 本文转载于:https://dev.to/charly3pins/maps-in-go-5a3j?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • 在Pandas中如何将年份和季度列合并为一个周期列?
    在Pandas中如何将年份和季度列合并为一个周期列?
    pandas data frame thing commans date lay neal and pree pree'和pree pree pree”,季度 2000 q2 这个目标是通过组合“年度”和“季度”列来创建一个新列,以获取以下结果: [python中的concate...
    编程 发布于2025-05-22
  • 在程序退出之前,我需要在C ++中明确删除堆的堆分配吗?
    在程序退出之前,我需要在C ++中明确删除堆的堆分配吗?
    在C中的显式删除 在C中的动态内存分配时,开发人员通常会想知道是否有必要在heap-procal extrable exit exit上进行手动调用“ delete”操作员,但开发人员通常会想知道是否需要手动调用“ delete”操作员。本文深入研究了这个主题。 在C主函数中,使用了动态分配变量(H...
    编程 发布于2025-05-22
  • 如何同步迭代并从PHP中的两个等级阵列打印值?
    如何同步迭代并从PHP中的两个等级阵列打印值?
    同步的迭代和打印值来自相同大小的两个数组使用两个数组相等大小的selectbox时,一个包含country代码的数组,另一个包含乡村代码,另一个包含其相应名称的数组,可能会因不当提供了exply for for for the uncore for the forsion for for ytry...
    编程 发布于2025-05-22
  • PHP未来:适应与创新
    PHP未来:适应与创新
    PHP的未来将通过适应新技术趋势和引入创新特性来实现:1)适应云计算、容器化和微服务架构,支持Docker和Kubernetes;2)引入JIT编译器和枚举类型,提升性能和数据处理效率;3)持续优化性能和推广最佳实践。 引言在编程世界中,PHP一直是网页开发的中流砥柱。作为一个从1994年就开始发展...
    编程 发布于2025-05-22
  • MySQL中如何高效地根据两个条件INSERT或UPDATE行?
    MySQL中如何高效地根据两个条件INSERT或UPDATE行?
    在两个条件下插入或更新或更新 solution:的答案在于mysql的插入中...在重复键更新语法上。如果不存在匹配行或更新现有行,则此功能强大的功能可以通过插入新行来进行有效的数据操作。如果违反了唯一的密钥约束。实现所需的行为,该表必须具有唯一的键定义(在这种情况下为'名称'...
    编程 发布于2025-05-22
  • 如何为PostgreSQL中的每个唯一标识符有效地检索最后一行?
    如何为PostgreSQL中的每个唯一标识符有效地检索最后一行?
    postgresql:为每个唯一标识符在postgresql中提取最后一行,您可能需要遇到与数据集合中每个不同标识的信息相关的信息。考虑以下数据:[ 1 2014-02-01 kjkj 在数据集中的每个唯一ID中检索最后一行的信息,您可以在操作员上使用Postgres的有效效率: id dat...
    编程 发布于2025-05-22
  • 如何在Chrome中居中选择框文本?
    如何在Chrome中居中选择框文本?
    选择框的文本对齐:局部chrome-inly-ly-ly-lyly solument 您可能希望将文本中心集中在选择框中,以获取优化的原因或提高可访问性。但是,在CSS中的选择元素中手动添加一个文本 - 对属性可能无法正常工作。初始尝试 state)</option> < op...
    编程 发布于2025-05-22
  • 为什么PYTZ最初显示出意外的时区偏移?
    为什么PYTZ最初显示出意外的时区偏移?
    与pytz 最初从pytz获得特定的偏移。例如,亚洲/hong_kong最初显示一个七个小时37分钟的偏移: 差异源利用本地化将时区分配给日期,使用了适当的时区名称和偏移量。但是,直接使用DateTime构造器分配时区不允许进行正确的调整。 example pytz.timezone(...
    编程 发布于2025-05-22
  • 使用jQuery如何有效修改":after"伪元素的CSS属性?
    使用jQuery如何有效修改":after"伪元素的CSS属性?
    在jquery中了解伪元素的限制:访问“ selector 尝试修改“:”选择器的CSS属性时,您可能会遇到困难。 This is because pseudo-elements are not part of the DOM (Document Object Model) and are th...
    编程 发布于2025-05-22
  • Go语言如何动态发现导出包类型?
    Go语言如何动态发现导出包类型?
    与反射软件包中的有限类型的发现能力相反,本文探索了替代方法,探索了在Runruntime。go import( “ FMT” “去/进口商” ) func main(){ pkg,err:= incorter.default()。导入(“ time”) 如果err...
    编程 发布于2025-05-22
  • 您如何在Laravel Blade模板中定义变量?
    您如何在Laravel Blade模板中定义变量?
    在Laravel Blade模板中使用Elegance 在blade模板中如何分配变量对于存储以后使用的数据至关重要。在使用“ {{}}”分配变量的同时,它可能并不总是最优雅的解决方案。幸运的是,Blade通过@php Directive提供了更优雅的方法: $ old_section =“...
    编程 发布于2025-05-22
  • 如何在JavaScript对象中动态设置键?
    如何在JavaScript对象中动态设置键?
    在尝试为JavaScript对象创建动态键时,如何使用此Syntax jsObj['key' i] = 'example' 1;不工作。正确的方法采用方括号: jsobj ['key''i] ='example'1; 在JavaScript中,数组是一...
    编程 发布于2025-05-22
  • PHP与C++函数重载处理的区别
    PHP与C++函数重载处理的区别
    作为经验丰富的C开发人员脱离谜题,您可能会遇到功能超载的概念。这个概念虽然在C中普遍,但在PHP中构成了独特的挑战。让我们深入研究PHP功能过载的复杂性,并探索其提供的可能性。在PHP中理解php的方法在PHP中,函数超载的概念(如C等语言)不存在。函数签名仅由其名称定义,而与他们的参数列表无关。...
    编程 发布于2025-05-22
  • input: Why Does "Warning: mysqli_query() expects parameter 1 to be mysqli, resource given" Error Occur and How to Fix It?

output: 解决“Warning: mysqli_query() 参数应为 mysqli 而非 resource”错误的解析与修复方法
    input: Why Does "Warning: mysqli_query() expects parameter 1 to be mysqli, resource given" Error Occur and How to Fix It? output: 解决“Warning: mysqli_query() 参数应为 mysqli 而非 resource”错误的解析与修复方法
    mysqli_query()期望参数1是mysqli,resource给定的,尝试使用mysql Query进行执行MySQLI_QUERY_QUERY formation,be be yessqli:sqli:sqli:sqli:sqli:sqli:sqli: mysqli,给定的资源“可能发...
    编程 发布于2025-05-22
  • 如何使用“ JSON”软件包解析JSON阵列?
    如何使用“ JSON”软件包解析JSON阵列?
    parsing JSON与JSON软件包 QUALDALS:考虑以下go代码:字符串 } func main(){ datajson:=`[“ 1”,“ 2”,“ 3”]`` arr:= jsontype {} 摘要:= = json.unmarshal([] byte(...
    编程 发布于2025-05-22

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3