」工欲善其事,必先利其器。「—孔子《論語.錄靈公》
首頁 > 程式設計 > Go 中的地圖

Go 中的地圖

發佈於2024-08-24
瀏覽:276

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]刪除
最新教學 更多>
  • 限制 Laravel 模型上的急切載重關係
    限制 Laravel 模型上的急切載重關係
    介紹 有時,當您渴望在 Laravel 模型上載入關係時,您可能想要限制返回的相關模型的數量。 例如,在部落格平台上,您可能想要載入系統中的每位作者及其三篇貼文。 在 Laravel 的舊版本中,限制急切加載的關係是一項有點繁瑣的任務。我從來沒有真正找到一種感覺正確的優雅方式來...
    程式設計 發佈於2024-11-07
  • 如何使用 GDB 在 C++ 中列印向量元素?
    如何使用 GDB 在 C++ 中列印向量元素?
    透過GDB 在C 中列印向量元素在GDB 中調試C 程式碼時,檢查std::vector 的內容可能具有挑戰性。例如,考慮一個名為 myVector 的 std::vector。我們如何有效地印製它的元素? 在 GCC 4.1.2 中,解決方案涉及存取向量的內部指標 myVector._M_impl...
    程式設計 發佈於2024-11-07
  • 如何在不同瀏覽器中自訂下拉清單寬度?
    如何在不同瀏覽器中自訂下拉清單寬度?
    IE 下拉清單寬度修改在Internet Explorer 中,下拉清單鏡像其保管箱的寬度,而在Firefox 中,它會適應內容。此限制需要擴展保管箱以容納最長的選擇,從而導致網頁美觀不美觀。 基於CSS 的可變寬度解決方案要克服此問題,使用CSS 允許下拉框和下拉列表使用不同的寬度,請考慮以下事項...
    程式設計 發佈於2024-11-07
  • 在 C++ 中格式化時如何右對齊輸出字串?
    在 C++ 中格式化時如何右對齊輸出字串?
    在C 中透過右對齊格式化輸出字串處理包含資料(例如座標)的文字檔案時,需要對齊列中的項目經常出現正確格式化的問題。在 C 中,輸出字串的操作對於實現這種對齊至關重要。本文解決了輸出字串右對齊的問題,提供了使用標準 C 技術的解決方案。 為了處理輸入文字文件,使用 line.split() 函數將每一...
    程式設計 發佈於2024-11-07
  • CSS 漸層產生器
    CSS 漸層產生器
    歡迎來到「免費 CSS 工具」系列。 在本系列中,我們將找到完全免費且易於使用的 CSS 工具。 在解釋瞭如何使用該工具後,我將與您分享該工具的連結。 工具連結:此工具可在 webdevtales.com 上取得 工具1:CSS漸層生成器 工具檢視: 介紹 歡迎使用 CSS 漸...
    程式設計 發佈於2024-11-07
  • 為什麼小型函數會讓你成為編碼英雄的原因
    為什麼小型函數會讓你成為編碼英雄的原因
    嘿,代碼愛好者們! ?您是否曾經發現自己迷失在無盡的線條海洋中,想知道一個功能在哪裡結束,另一個功能從哪裡開始?我們都去過那裡。今天,我們來談談為什麼將程式碼分解成更小的、可管理的區塊不僅僅是一種最佳實踐——它還能改變你的開發技能和職業生涯。 1.未來的你會感謝你 想像一下:現在是...
    程式設計 發佈於2024-11-07
  • JavaScript 變數名稱中美元符號的含義是什麼?
    JavaScript 變數名稱中美元符號的含義是什麼?
    為什麼在 JavaScript 變數名稱中使用美元符號? 提供的 JavaScript 程式碼包含一個名為「$item」的變量,該變數引發問題:變數名稱中美元符號的用途是什麼? 在 JavaScript 中,變數名稱前面的美元符號對於解譯器來說沒有特殊意義。它用作輕鬆識別包含 jQuery 物件的變...
    程式設計 發佈於2024-11-07
  • Laravel 中的授權 - 初學者指南
    Laravel 中的授權 - 初學者指南
    掌握 Laravel 中的授權:Gates 與策略類別 ?? 在現代 Web 應用程式中,控制誰可以存取或修改資源至關重要。例如,在部落格應用程式中,您可能希望確保只有貼文的擁有者才能編輯或刪除它。 Laravel 提供了兩種優雅的方式來處理授權:Gates 和 Policy Cl...
    程式設計 發佈於2024-11-07
  • Laravel 的枚舉
    Laravel 的枚舉
    報告 在我從事的一個專案中,有一個選擇欄位定義了不會更改的值。因此,為了列出此選擇中的項目,我決定建立一個枚舉類,然後描述這些值。但是,該項目需要支援英語和西班牙語,並且選擇選項的文本需要適應這一點,同時又不丟失對相應枚舉項的引用。換句話說,如果我選擇了“馬”這個項目,我需要係統知道這個項目仍然是“...
    程式設計 發佈於2024-11-07
  • \“模組 vs 主要:現代英雄 vs package.json 的復古傳奇!\”
    \“模組 vs 主要:現代英雄 vs package.json 的復古傳奇!\”
    什麼是模組字段? package.json 中的 module 欄位指定 ESM(ES6 模組) 的入口點。與為CommonJS 模組(require()) 設計的main 欄位不同,模組用於支援較新的ESM 標準的目標環境,例如JavaScript 捆綁程式(Webpack、Ro...
    程式設計 發佈於2024-11-07
  • 如何在 CSS 檔案中實現類似變數的行為?
    如何在 CSS 檔案中實現類似變數的行為?
    CSS 檔案中的變數宣告並使用在 CSS 中,通常需要在整個樣式表中重複使用特定值。雖然沒有明確的變數聲明語法,但有一些技術可以實現此功能。 一種方法是利用 CSS 選擇器和樣式規則。將相關樣式組合在單一規則下,您可以避免重複,同時澄清每種樣式的範圍。例如:/* Theme color: text ...
    程式設計 發佈於2024-11-07
  • 如何在 PHP 中編寫基本函數來從文字中刪除表情符號?
    如何在 PHP 中編寫基本函數來從文字中刪除表情符號?
    用 PHP 編寫一個簡單的 removeEmoji 函數處理線上文字通常需要刪除表情符號,特別是在 Instagram 評論等情況下。本文探討了針對這種需求的解決方案,利用 PHP preg_replace 函數來有效地消除給定文字中的表情符號。 removeEmoji 函數利用一系列正規表示式來匹...
    程式設計 發佈於2024-11-07
  • Slim 和 Flight PHP 框架比較
    Slim 和 Flight PHP 框架比較
    为什么要使用微框架? 在社交媒体上,新的 PHP 开发人员经常会问“我的项目应该使用什么框架”,通常给出的答案是“Laravel”或“Symfony”。 虽然这些都是不错的选择,但这个问题的正确答案应该是“你需要框架做什么?” 正确的框架应该能够满足您的需要,并且不会包含大量您永远...
    程式設計 發佈於2024-11-07
  • 如何建立您的第一個 Python 遊戲:使用 PyGame 創建簡單射擊遊戲的逐步指南
    如何建立您的第一個 Python 遊戲:使用 PyGame 創建簡單射擊遊戲的逐步指南
    Hi lovely readers, Have you ever wanted to create your own video game? Maybe you’ve thought about building a simple shooter game where you can move ar...
    程式設計 發佈於2024-11-07
  • 為什麼我的 Java JDBC 程式碼在連接到 Oracle 時拋出“IO 錯誤:網路適配器無法建立連線”?
    為什麼我的 Java JDBC 程式碼在連接到 Oracle 時拋出“IO 錯誤:網路適配器無法建立連線”?
    診斷Oracle JDBC「IO 錯誤:網路適配器無法建立連線」嘗試使用JDBC 執行簡單的Java 程式碼時要連線到Oracle資料庫,您可能會遇到神秘的錯誤「IO 錯誤:網路適配器無法建立連線」。這個令人費解的消息源於 JDBC 驅動程式的模糊術語,並且可能由各種根本原因造成。以下是一些可能導致...
    程式設計 發佈於2024-11-07

免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。

Copyright© 2022 湘ICP备2022001581号-3