Go语言 映射(map)

简介: 1. 什么是 map2. 创建 map3. 访问 map4. nil map和空map5. map中元素的返回值6. len()和delete()7. 测试map中元素是否存在8. 迭代遍历 map9. 获取map中所有的key10. 传递map给函数

1. 什么是 map


Map 是一种无序的键值对的集合。Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值


Map 是无序的,我们无法决定它的返回顺序,这是因为 Map 是使用 hash 表来实现的

Map 是引用类型,必须初始化才能使用。其中,key的类型除了切片等引用类型,其他类型都可以;而value则可使用所有类型的值

 

2. 创建 map


可以通过make()创建map,它会先创建好底层数据结构,然后再创建map,并让map指向底层数据结构


my_map := make(map[string]int)


[string]表示map的key的数据类型

int表示key对应的值

 

直接通过大括号创建并初始化赋值:

// 空map
my_map := map[string]string{}
// 初始化赋值
my_map := map[string]string{"Red": "#da1337","Orange": '#e95a22"}
// 格式化赋值
my_map := map[string]int{
"Java":11,
"Perl":8,
"Python":13, // 注意结尾的逗号不能少
}


注意:


其中map的key可以是任意内置的数据类型(如int),或者其它可以通过 == 进行等值比较的数据类型,如interface和指针可以,slice、数组、map、struct类型都不能作为key ,并且key必须唯一。


但value基本可以是任意类型,例如嵌套一个slice到map中:

my_map := map[string][]int{}


3. 访问 map


访问map中的元素时,指定它的key即可,注意string类型的key必须加上引号:

package main
import "fmt"
func main() {
 my_map := map[string]int{
  "1": 10,
  "2": 20,
  "3": 30,
  "4": 40,
 }
 //访问
 fmt.Println(my_map["1"])
 fmt.Println(my_map)
 fmt.Println("")
 //赋值已有的key & value
 my_map["2"] = 50
 fmt.Println(my_map["2"])
 fmt.Println(my_map)
 fmt.Println("")
 //赋值新的key&value
 my_map["5"] = 66
 fmt.Println(my_map["5"])
 fmt.Println(my_map)
}
//输出结果
10
map[1:10 2:20 3:30 4:40]
50
map[1:10 2:50 3:30 4:40]
66
map[1:10 2:50 3:30 4:40 5:66]


4. nil map和空map



空map是不做任何赋值的map:

// 空map
package main
import "fmt"
func main() {
 my_map := map[string]string{}
 fmt.Println(my_map)
}
//输出结果
map[]
nil map,它将不会做任何初始化,不会指向任何数据结构:
// nil map
var my_map map[string]string

nil map和empty map的关系,就像nil slice和empty slice一样,两者都是空对象,未存储任何数据,但前者不指向底层数据结构,后者指向底层数据结构,只不过指向的底层对象是空对象。


使用println输出看下即可知道:


package main
func main() {
 var nil_map map[string]string
 println(nil_map)
 emp_map := map[string]string{}
 println(emp_map)
}


输出结果:

0x0

0xc04204de38

所以,map类型实际上就是一个指针。

 

5. map中元素的返回值


当访问map中某个元素的时候,有两种返回值的格式:

value := my_map["key"]

value,exists := my_map["key"]

第一种很好理解,就是检索map中key对应的value值。如果key不存在,则value返回值对应数据类型的0。例如int为数值0,布尔为false,字符串为空""。

第二种不仅返回key对应的值,还根据key是否存在返回一个布尔值赋值给exists变量。所以,当key存在时,value为对应的值,exists为true;当key不存在,value为0(同样是各数据类型所代表的0),exists为false。

看下例子:


上面将输出如下结果:


10

20 true

0 false

在Go中设置类似于这种多个返回值的情况很多,即便是自己编写函数也会经常设置它的exists属性。


6. len()和delete()


len()函数用于获取map中元素的个数,即有多个少key。delete()用于删除map中的某个key。

package main
import "fmt"
func main() {
 my_map := map[string]int{
  "1": 10,
  "2": 20,
  "3": 30,
  "4": 40,
 }
 fmt.Printf("删除前长度为%d\n", len(my_map))
 delete(my_map, "1")
 fmt.Printf("删除后长度为%d", len(my_map))
}
//输出结果如下
删除前长度为4
删除后长度为3


7. 测试map中元素是否存在


两种方式可以测试map中是否存在某个key:

① 根据map元素的第二个返回值来判断

② 根据返回的value是否为0(不同数据类型的0不同)来判断

方式一:直接访问map中的该元素,将其赋值给两个变量,第二个变量就是元素是否存在的修饰变量。

package main
import "fmt"
func main() {
 my_map := map[string]int{
  "1": 10,
  "2": 20,
  "3": 30,
  "4": 40,
 }
//方法1
/* value, exists := my_map["1"]
 if exists {
  fmt.Println("存在", value)
 }
 */
 //方法2
 if value, exists := my_map["1"]; exists {
  fmt.Printf("值存在, value=%d", value)
 }
}
//输出结果如下
值存在, value=10
方式二:根据map元素返回的value判断。因为该map中的value部分是int类型,所以它的0是数值的0。
package main
import "fmt"
func main() {
 my_map := map[string]int{
  "1": 10,
  "2": 20,
  "3": 30,
  "4": 40,
 }
 value := my_map["5"]
 if value == 0 {
  fmt.Println("不存在")
 }
}
//输出结果如下
不存在


如果map的value数据类型是string,则判断是否为空:


package main
import "fmt"
func main() {
 my_map := map[string]string{
  "1": "book",
  "2": "games",
  "3": "computer",
 }
 value := my_map["5"]
 if value == "" {
  fmt.Println("不存在")
 }
}
//输出结果如下
不存在

由于map中的value有可能本身是存在的,但它的值为0,这时就会出现误判断。例如下面的"3",它已经存在,但它对应的值为0


package main
import "fmt"
func main() {
 my_map := map[string]int{
  "1": 22,
  "2": 11,
  "3": 0,
 }
 value := my_map["3"]
 if value == 0 {
  fmt.Println("不存在")
 }
}
//输出结果如下
不存在


所以,应当使用第一种方式进行判断元素是否存在。

 

8. 迭代遍历 map


因为map是key/value类型的数据结构,key就是map的index,所以range关键字对map操作时,将返回key和value。

package main
import "fmt"
func main() {
 my_map := map[string]int{
  "1": 22,
  "2": 11,
  "3": 0,
  "4": 55,
  "5": 66,
 }
 for k, v := range my_map {
  fmt.Printf("key=%s, value=%d\n", k, v)
 }
}
//输出结果如下
key=1, value=22
key=2, value=11
key=3, value=0
key=4, value=55
key=5, value=66


如果range迭代map时,只给一个返回值,则表示迭代map的key:


package main
import "fmt"
func main() {
 my_map := map[string]int{
  "1": 22,
  "2": 11,
  "3": 0,
  "4": 55,
  "5": 66,
 }
 for key := range my_map {
  fmt.Println("key=", key)
 }
}
//输出结果
key= 1
key= 2
key= 3
key= 4
key= 5



9. 获取map中所有的key


Go中没有提供直接获取map所有key的函数。所以,只能自己写,方式很简单,range遍历map,将遍历到的key放进一个slice中保存起来。

package main
import "fmt"
func main() {
 my_map := map[string]int{
  "Java":   11,
  "Perl":   8,
  "Python": 13,
  "Shell":  23,
 }
    // 保存map中key的slice
    // slice类型要和map的key类型一致
 keys := make([]string,0,len(my_map))
    // 将map中的key遍历到keys中
 for map_key,_ := range my_map {
  keys = append(keys,map_key)
 }
 fmt.Println(keys)
}


注意上面声明的slice中要限制长度为0,否则声明为长度4、容量4的slice,而这4个元素都是空值,而且后面append()会直接对slice进行一次扩容,导致append()后的slice长度为map长度的2倍,前一半为空,后一般才是map中的key。

 

10. 传递map给函数


map是一种指针,所以将map传递给函数,仅仅只是复制这个指针,所以函数内部对map的操作会直接修改外部的map。

例如,test()用于给map的key对应的值加1。

package main
import "fmt"
func main() {
 my_map := map[string]int{
  "1": 22,
  "2": 11,
  "3": 0,
  "4": 55,
  "5": 66,
 }
 fmt.Println("修改之前key=", my_map["3"])
 fmt.Println(my_map)
 fmt.Println("")
 test(my_map, "3")
 fmt.Println("修改之后key=", my_map["3"])
 fmt.Println(my_map)
}
func test(m map[string]int, key string) {
 m[key] += 1
}
//输出结果如下
修改之前key= 0
map[1:22 2:11 3:0 4:55 5:66]
修改之后key= 1
map[1:22 2:11 3:1 4:55 5:66]





相关文章
|
1天前
|
安全 Go 调度
Go语言中的并发编程
Go语言自带了强大的并发编程能力,它的协程机制可以让程序轻松地实现高并发。本文将从并发编程的基础概念出发,介绍Go语言中的协程机制、通道和锁等相关知识点,帮助读者更好地理解并发编程在Go语言中的实践应用。
|
3天前
|
Ubuntu Unix Linux
【GO基础】1. Go语言环境搭建
【GO基础】1. Go语言环境搭建
|
4天前
|
JSON 前端开发 Go
lucky - go 语言实现的快速开发平台
go 语言实现的快速开发平台,自动生成crud代码,前端页面通过json配置,无需编写前端代码。
10 0
|
5天前
|
存储 Java Go
Go 语言切片如何扩容?(全面解析原理和过程)
Go 语言切片如何扩容?(全面解析原理和过程)
14 2
|
5天前
|
负载均衡 Go 调度
使用Go语言构建高性能的Web服务器:协程与Channel的深度解析
在追求高性能Web服务的今天,Go语言以其强大的并发性能和简洁的语法赢得了开发者的青睐。本文将深入探讨Go语言在构建高性能Web服务器方面的应用,特别是协程(goroutine)和通道(channel)这两个核心概念。我们将通过示例代码,展示如何利用协程处理并发请求,并通过通道实现协程间的通信和同步,从而构建出高效、稳定的Web服务器。
|
5天前
|
算法 Go 分布式数据库
构建高可用的分布式数据库集群:使用Go语言与Raft共识算法
随着数据量的爆炸式增长,单一数据库服务器已难以满足高可用性和可扩展性的需求。在本文中,我们将探讨如何使用Go语言结合Raft共识算法来构建一个高可用的分布式数据库集群。我们不仅会介绍Raft算法的基本原理,还会详细阐述如何利用Go语言的并发特性和网络编程能力来实现这一目标。此外,我们还将分析构建过程中可能遇到的挑战和解决方案,为读者提供一个完整的实践指南。
|
5天前
|
消息中间件 Go API
基于Go语言的微服务架构实践
随着云计算和容器化技术的兴起,微服务架构成为了现代软件开发的主流趋势。Go语言,以其高效的性能、简洁的语法和强大的并发处理能力,成为了构建微服务应用的理想选择。本文将探讨基于Go语言的微服务架构实践,包括微服务的设计原则、服务间的通信机制、以及Go语言在微服务架构中的优势和应用案例。
|
5天前
|
安全 测试技术 数据库连接
使用Go语言进行并发编程
【5月更文挑战第15天】Go语言以其简洁语法和强大的并发原语(goroutines、channels)成为并发编程的理想选择。Goroutines是轻量级线程,由Go运行时管理。Channels作为goroutine间的通信机制,确保安全的数据交换。在编写并发程序时,应遵循如通过通信共享内存、使用`sync`包同步、避免全局变量等最佳实践。理解并发与并行的区别,有效管理goroutine生命周期,并编写测试用例以确保代码的正确性,都是成功进行Go语言并发编程的关键。
|
5天前
|
数据采集 监控 Java
Go语言并发编程:Goroutines和Channels的详细指南
Go语言并发编程:Goroutines和Channels的详细指南
12 3
|
5天前
|
数据采集 人工智能 搜索推荐
快速入门:利用Go语言下载Amazon商品信息的步骤详解
本文探讨了使用Go语言和代理IP技术构建高效Amazon商品信息爬虫的方法。Go语言因其简洁语法、快速编译、并发支持和丰富标准库成为理想的爬虫开发语言。文章介绍了电商网站的发展趋势,如个性化推荐、移动端优化和跨境电商。步骤包括设置代理IP、编写爬虫代码和实现多线程采集。提供的Go代码示例展示了如何配置代理、发送请求及使用goroutine进行多线程采集。注意需根据实际情况调整代理服务和商品URL。
快速入门:利用Go语言下载Amazon商品信息的步骤详解