go基础07-了解map实现原理并高效使用

这篇具有很好参考价值的文章主要介绍了go基础07-了解map实现原理并高效使用。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

对于C程序员出身的Gopher来说,map类型是和切片、interface一样能让他们感受到Go语言先进性的重要语法元素。map类型也是Go语言中最常用的数据类型之一。

go 中 map 怎么表现?

一些有关Go语言的中文教程或译本将map称为字典或哈希表,但在这里我选择不译,直接使用map。map是Go语言提供的一种抽象数据类型,它表示一组无序的键值对(key-value,后续我们会直接使用key和value分别表示键和值)。

map类型不支持“零值可用”,未显式赋初值的map类型变量的零值为nil。对处于零值状态的map变量进行操作将会导致运行时panic:

var m map[string]int // m = nil
m["key"] = 1 // panic: assignment to entry in nil map

简单来说就是不能不赋值,只对key 赋值是不行的。

我们必须对map类型变量进行显式初始化后才能使用它。
和切片一样,创建map类型变量有两种方式:

  • 一种是使用复合字面值,
1)使用复合字面值创建map类型变量
// $GOROOT/src/net/status.go
var statusText = map[int]string{
StatusOK: "OK",
StatusCreated: "Created",
StatusAccepted: "Accepted",
...
}
  • 一种是使用make这个预声明的内置函数。
2)使用make创建map类型变量
// $GOROOT/src/net/client.go
icookies = make(map[string][]*Cookie)
// $GOROOT/src/net/h2_bundle.go
http2commonLowerHeader = make(map[string]string, len(common))

和切片一样,map也是引用类型,将map类型变量作为函数参数传入不会有很大的性能损耗,并且在函数内部对map变量的修改在函数外部也是可见的,比如下面的例子:

func foo(m map[string]int) {
	m["key1"] = 11
	m["key2"] = 12
}
func main() {
m := map[string]int{
	"key1": 1,
	"key2": 2,
}
 fmt.Println(m) // map[key1:1 key2:2]
foo(m)
fmt.Println(m) // map[key1:11 key2:12]
}

map的基本操作

1. 插入数据

面对一个非nil的map类型变量,我们可以向其中插入符合map类型定义的任意键值对。
Go运行时会负责map内部的内存管理,因此除非是系统内存耗尽,我们不用担心向map中插入数据的数量。

m := make(map[K]V)
m[k1] = v1
m[k2] = v2
m[k3] = v3

如果key已经存在于map中,则该插入操作会用新值覆盖旧值:

m := map[string]int {
"key1" : 1,
"key2" : 2,
}
m["key1"] = 11 // 11会覆盖掉旧值1
m["key3"] = 3 // map[key1:11 key2:2 key3:3]

2. 获取数据个数

和切片一样,map也可以通过内置函数len获取当前已经存储的数据个数:

m := map[string]int {
"key1" : 1,
"key2" : 2,
}
fmt.Println(len(m)) // 2
m["key3"] = 3
fmt.Println(len(m)) // 3

3. 查找和数据读取

map类型更多用在查找和数据读取场合。所谓查找就是判断某个key是否存在于某个map
中。我们可以使用“comma ok”惯用法来进行查找

_, ok := m["key"]
if !ok {
// "key"不在map中
}

这里我们并不关心某个key对应的value,而仅仅关心某个key是否在map中,因此我们
使用空标识符(blank identifier)忽略了可能返回的数据值,而仅关心ok的值是否为
true(表示在map中)。

如果要读取key对应的value的值,我们可能会写出下面这样的代码:

m := map[string]int
m["key1"] = 1
m["key2"] = 2
v := m["key1"]
fmt.Println(v) // 1
v = m["key3"]
fmt.Println(v) // 0

上面的代码在key存在于map中(如“key1”)的情况下是没有问题的。但是如果key不
存在于map中(如“key3”),我们看到v仍然被赋予了一个“合法”值0,这个值是value
类型int的零值。在这样的情况下,我们无法判定这个0是“key3”对应的值还是
因“key3”不存在而返回的零值。为此我们还需要借助“comma ok”惯用法

m := map[string]int
v, ok := m["key"]
if !ok {
// "key"不在map中
}
fmt.Println(v)

我们需要通过ok的值来判定key是否存在于map中。只有当ok = true时,所获得的
value值才是我们所需要的。综上,Go语言的一个最佳实践是总是使用“comma ok”惯用法读取map中的值。

4. 删除数据

我们借助内置函数delete从map中删除数据:

m := map[string]int {
"key1" : 1,
"key2" : 2,
}
fmt.Println(m) // map[key1:1 key2:2]
delete(m, "key2")
fmt.Println(m) // map[key1:1]

注意,即便要删除的数据在map中不存在,delete也不会导致panic。

5. 遍历数据

我们可以像对待切片那样通过for range语句对map中的数据进行遍历:

func main() {
m := map[int]int{
1: 11,
 2: 12,
3: 13,
}
fmt.Printf("{ ")
for k, v := range m {
fmt.Printf("[%d, %d] ", k, v)
}
fmt.Printf("}\n")
}

我们看到对同一map做多次遍历,遍历的元素次序并不相同。这是因为Go运行时在初始
化map迭代器时对起始位置做了随机处理。因此千万不要依赖遍历map所得到的元素次序。

如果你需要一个稳定的遍历次序,那么一个比较通用的做法是使用另一种数据结构来
按需要的次序保存key,比如切片:
文章来源地址https://www.toymoban.com/news/detail-704954.html

import "fmt"
func doIteration(sl []int, m map[int]int) {
fmt.Printf("{ ")
for _, k := range sl { // 按切片中的元素次序迭代
v, ok := m[k]
if !ok {
continue
}
fmt.Printf("[%d, %d] ", k, v)
}
fmt.Printf("}\n")
}
func main() {
var sl []int
m := map[int]int{
1: 11,
2: 12,
3: 13,
}
for k, _ := range m {
sl = append(sl, k) // 将元素按初始次序保存在切片中
}
for i := 0; i < 3; i++ {
doIteration(sl, m)
}
}
$go run map_stable_iterate.go
{ [1, 11] [2, 12] [3, 13] }
{ [1, 11] [2, 12] [3, 13] }
{ [1, 11] [2, 12] [3, 13] }

到了这里,关于go基础07-了解map实现原理并高效使用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 编程笔记 Golang基础 007 第一个程序:hello world 使用Goland

    开始在Goland环境中编程go语言代码啦。 打开GoLand软件。 选择 “File”(文件)菜单,然后点击 “New Project”(新建项目)或使用快捷键 Ctrl+Shift+A 并搜索 “New Project”。 在新建项目向导中,选择 “Go” 并点击 “Next” 按钮。 配置项目设置: 为项目选择一个合适的保存位置。

    2024年02月20日
    浏览(42)
  • goland setup go env

    go env -w设置的变量,在goland中不生效,需要额外配置。 点击goland-preference,在go module里,设置go环境变量即可。

    2024年02月08日
    浏览(54)
  • 小白学go基础03-了解Go项目的项目结构

    我们先来看看第一个Go项目——Go语言自身——的项目结构是什么样的。Go项目的项目结构自1.0版本发布以来一直十分稳定,直到现在Go项目的顶层结构基本没有大的改变。 截至Go项目commit 1e3ffb0c(2019.5.14), Go1.0 项目结构如下: 作为Go语言的创世项目,Go的项目结构的布局对

    2024年02月09日
    浏览(51)
  • go基础10 -字符串的高效构造与转换

    前面提到过,Go原生支持通过+/+=操作符来连接多个字符串以构造一个更长的字符串,并且通过+/+=操作符的字符串连接构造是最自然、开发体验最好的一种。 但Go还提供了其他一些构造字符串的方法,比如: ● 使用fmt.Sprintf; ● 使用strings.Join; ● 使用strings.Builder; ● 使用

    2024年02月09日
    浏览(36)
  • 【Go】Goland下载与安装教程(详细)

    一、GoLand官网下载安装 开发环境:https://www.jetbrains.com/go/ 软件激活ToolBox: 一键Activate,激活后出现success,开发工具即可直接使用: 二、下载go语言sdk(go的标准库) 下载地址:https://golang.google.cn/dl/ 三、创建工程目录 创建一个工程目录,如 D:SoftwaresGOGoWorkstation ,这个目录

    2024年02月04日
    浏览(43)
  • 新手小白需要了解的 Go 基础细节杂谈

    Golang 基础知识一遍过 👈 今日记录一下 学习 golang 这门语言遇到的一些比较特殊的细节,供大家参考。        所以,在我们输出内容的时候,可以包含很多的非 ASCII 码字符。实际上,Go 是天生支持 UTF-8 的,任何字符都可以直接输出,甚至可以使用 UTF-8 中的任何字符作为标

    2023年04月09日
    浏览(41)
  • MacOS goland go1.21 debug问题

    brew install dlv 安装之后在终端会显示所在目录 类似/usr/local/Cellar/delve/1.21.0/bin 在文件系统中找到goland 右击选择show package contents - Contents - plugins - go 尝试替换 其中对应系统 的 dlv 结果还是不行 然后打开应用goland Help → Edit Custom Properties 增加以下代码: dlv.path=/usr/local/Cellar/del

    2024年02月11日
    浏览(37)
  • C++进阶--使用哈希表实现unordered_map和unordered_set的原理与实例

    本文将介绍如何使用哈希表来实现C++ STL库中的unordered_map和unordered_set容器。我们将会解释哈希表的基本原理,并给出具体的代码示例,帮助读者更好地理解和应用哈希表。 哈希原理讲解–链接入口 set和map的实现的文章,与unordered_map实现类似 unordered_set是一种集合存储的容器

    2024年04月09日
    浏览(44)
  • Go语言集成开发环境(IDE):GoLand 2023中文

    GoLand 2023是一款由JetBrains开发的现代化、功能丰富的Go语言集成开发环境(IDE) 。它提供了智能代码提示和自动完成、强大的内置调试器以及代码重构工具,帮助开发者提高编码效率并确保代码质量。GoLand 2023还支持多种版本控制系统,集成了测试工具,并提供了代码审查功能

    2024年02月06日
    浏览(48)
  • 【Go进阶】怎么实现并发安全的map

    go语言提供的数据类型中,只有channel是并发安全的,基础map并不是并发安全的。以下三种方案实现了并发安全的map。 实现原理: 给map添加一把读写锁,读操作加读锁进行读取;添加,更新,删除,遍历,获取长度这些操作加写锁后在进行操作。 代码实现: 以下代码是并发

    2024年02月03日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包