Go语言基础之map

1 map概述

哈希表是一种巧妙并且使用的数据结构。它是一个无序的key-value对的集合,其中所有的key都是不同的,然后通过给定的key可以在常数时间复杂度内检索、更新或删除对应的value。

Golang中,一个map就是一个哈希表的引用,又称为字段或者关联数组。类似其他编程语言的集合,在编程中是经常使用到。

2 map的声明

2.1 基本语法

var map 变量名 map[keytype]valuetype
  • key的类型

    Golang中的map的key可以是很多种类型,比如bool,数字,string,指针,channel,还可以是接口,结构体,数组。key对应的类型必须支持==比较运算符的数据类型,所以map可以通过测试key是否相等来判断是否已经存在。

    通常key为int、string。

    注意:slice,map还有function不可以作为key,因为这几个没法用==来判断。

  • valuetype的类型

    valuetype的类型没有任何的限制

    通常为:数字(整数,浮点数),string,map,struct。

2.2 map声明举例

var a map[string]string
var a map[string]int
var a map[int]string
var a map[string]map[string]string

注意:声明是不会分配内存的,初始化需要使用make,分配内存后才能赋值和使用。内建函数make分配并初始化一个类型为切片、映射、或通道的对象。

map声明案例演示:

func main(){
    var a map[string]string
    a = make(map[string]string, 10)  //使用make函数给map分配数据空间
    a["no1"] = "tom"
    a["no2"] = "jerry"
    a["no3"] = "jack"
    a["no1"] = "alice"
    fmt.Println(a)
}

对上面代码的说明:

  • map在使用前一定要make

  • map的key是不能重复的。如果重复了,则以最后这个key-value为准

  • map的value是可以相同的

  • map的key-value是无序的

3 map的使用

方式一:

var a map[string]string
//在使用map前,需要先make,make的作用是给map分配数据空间
a = make(map[string]string, 10)
a["no1"] = "tom"
a["no2"] = "jerry"
a["no3"] = "jack"
a["no1"] = "alice"
fmt.Println(a)

方式二:

cities := make(map[string]string)
cities["no1"] = "北京"
cities["no2"] = "天津"
cities["no3"] = "上海"
fmt.Println(cities)

方式三:

heroes := map[string]string{
    "heroes1" : "钢铁侠"
    "heroes2" : "蜘蛛侠"
    "heroes3" : "绿巨人"
}
heroes["heroes4"] = "黑寡妇"
fmt.Println("heroes=", heroes)

案例演示:

案例需求:存放三个学生的信息,每个学生有name和sex信息

students := make(map[string]map[string]string, 3)

//对于map要先使用make声明,students中的value也是map,所以对于每个key对应的值都需要map
students["stu01"] = make(map[string]string, 2)
students["stu01"]["name"] = "tom"
students["stu01"]["sex"] = "男"

students["stu02"] = make(map[string]string, 2)
students["stu02"]["name"] = "mary"
students["stu02"]["sex"] = "女"

fmt.Println(students)
fmt.Println(students["stu01"])
fmt.Println(students["stu01"]["name"])

4 map的增删改查操作

4.1 map增加和更新

map["key"] = value  //如果key还没有,就是增加,如果key存在就是修改

4.2 删除

删除map中的一个键值对,使用内置函数delete。使用方法:delete(map, "key"),如果key存在,就删除key-value,如果不存在,不操作,但也不会报错。

cities := make(map[string]string)
cities["no1"] = "北京"
cities["no2"] = "天津"
cities["no3"] = "上海"
fmt.Println(cities)

//使用delete内置函数删除cities中的键值对
delete(cities, "no1")
fmt.Println(cities)

delete(cities, "no4")
fmt.Println(cities)

在Golang中没有一下删除整个map中的key的函数。这时可以遍历一下key逐个删除,或者map = make(...),make一个新的,让原来的成为垃圾,被gc回收。

//方法一:使用make,开辟一个新的map空间
cities = make(map[string]string)
fmt.Println(cities)

//方法二:遍历key,逐个删除
for key, _ := range cities {
    delete(cities, cities[key])
}

4.3 查找

map的查找还是通过key来进行查找,我们可以封装一个函数,参数为key和map,返回值为bool类型;如果找到返回true,否则返回false。代码如下:

func findRes(key string, m map[string]string) bool {
	_, ok := m[key]		// 判断某个键是否存在
	if ok {
		return true
	} else {
		return false
	}
}

func main() {
	cities := make(map[string]string)
	cities["no1"] = "北京"
	cities["no2"] = "天津"
	cities["no3"] = "上海"
	fmt.Println(cities)
	fmt.Println(findRes("no1", cities))
	fmt.Println(findRes("no4", cities))
}

4.4 遍历

map的遍历使用使用for-range的结构遍历

func main() {

	cities := make(map[string]string)
	cities["no1"] = "北京"
	cities["no2"] = "天津"
	cities["no3"] = "上海"
	fmt.Println(cities)
	for k, v := range cities {
		fmt.Printf("k=%v v=%v
", k, v)
	}

	//变量map的map
	students := make(map[string]map[string]string, 3)

	//对于map要先使用make声明,students中的value也是map,所以对于每个key对应的值都需要map
	students["stu01"] = make(map[string]string, 2)
	students["stu01"]["name"] = "tom"
	students["stu01"]["sex"] = "男"

	students["stu02"] = make(map[string]string, 2)
	students["stu02"]["name"] = "mary"
	students["stu02"]["sex"] = "女"

	for key1, value1 := range students {
		fmt.Println("key1=", key1)
		for key2, value2 := range value1 {
			fmt.Printf("	key2=%v value2=%v
", key2, value2)
		}
	}
}

使用len() 内建函数可以返回map的长度:

func main() {

	cities := make(map[string]string)
	cities["no1"] = "北京"
	cities["no2"] = "天津"
	cities["no3"] = "上海"
	fmt.Println(cities)

	//变量map的map
	students := make(map[string]map[string]string, 3)

	//对于map要先使用make声明,students中的value也是map,所以对于每个key对应的值都需要map
	students["stu01"] = make(map[string]string, 2)
	students["stu01"]["name"] = "tom"
	students["stu01"]["sex"] = "男"

	students["stu02"] = make(map[string]string, 2)
	students["stu02"]["name"] = "mary"
	students["stu02"]["sex"] = "女"

	fmt.Println(len(students))		//2
	fmt.Println(len(cities))		//3
}

5 map切片

5.1 基本介绍

切片的数据类型如果是map,则称为slice of map(map切片),这样map的个数就可以动态变化。

5.2 map切片的使用

案例演示:

案例需求:使用一个map来记录学生的信息name和age,并且学生的个数可以动态增加

func main() {
	//声明一个students的map切片
	var students []map[string]string
	students = make([]map[string]string, 2)		//先存放两个学生
	if students[0] == nil {
		students[0] = make(map[string]string, 2)
		students[0]["name"] = "tom"
		students[0]["age"] = "24"
	}
	if students[1] == nil {
		students[1] = make(map[string]string, 2)
		students[1]["name"] = "mary"
		students[1]["age"] = "20"
	}

	//越界panic: runtime error: index out of range [2] with length 2
	//if students[2] == nil {
	//	students[2] = make(map[string]string, 2)
	//	students[2]["name"] = "jerry"
	//	students[2]["age"] = "20"
	//}

	//使用append函数动态的增加students
	student := map[string]string {
		"name" : "jerry",
		"age" : "20",
	}
	students = append(students, student)
	fmt.Println(students)	//[map[age:24 name:tom] map[age:20 name:mary] map[age:20 name:jerry]]
}

6 map排序

6.1 基本介绍

Golang中没有一个专门的方法或者函数针对map的key进行排序。Golang中的map默认是无序的,注意也不是按照添加的顺序存放的,每次遍历,得到的输出可能不一样。

map排序方法:先将key进行排序,然后根据key值遍历输出即可

6.2 map排序演示

func main() {
	rand.Seed(time.Now().UnixNano())	//初始化随机种子
	// 声明一个scoreMap用来存学生的成绩
	var scoreMap = make(map[string]int, 200)

	for i := 0; i <  100; i++ {
		key := fmt.Sprintf("stu%02d", i)	//生成stu开头的字符串
		scoreMap[key] = rand.Intn(100)			//生成0-100之间的随机数作为学生的成绩
	}

	var keys = make([]string, 0, 100)
	//取出scoreMap中的key存储到[]key切片中
	for key := range scoreMap {
		keys = append(keys, key)
	}

	//对切片进行排序
	sort.Strings(keys)

	//使用排序好的key对scoreMap进行遍历
	for _, k := range keys {
		fmt.Println(k, scoreMap[k])
	}
}

7 map使用细节

  • map是引用类型,遵守引用类型传递的机制,在一个函数接收map,修改之,则会修改原来的map

  • map的容量达到后,再想map增加元素,会自动扩容,并不会发生panic,也就是说map能动态的增长key-value

  • map的value也经常使用struct类型,更适合管理复杂的数据

type Stu struct {
	name string
	age int
}

func main() {
	// 使用map来存储Stu结构体
	// 学生的学号作为map的key,因为学号唯一,学生的姓名和年龄作为Stu结构体的字段
	var stuMap = make(map[string]Stu, 10)
	stuMap["no1"] = Stu{"tom", 19}
	stuMap["no2"] = Stu{"jerry", 20}

	fmt.Println(stuMap)		// map[no1:{tom 19} no2:{jerry 20}]
}

8 练习

  • 使用map[string]map[string]string的map类型,key表示用户名,是唯一的,不可以重复;如果这个用户名存在,就将其密码修改成"666666",如果不存在就增加这个用户的信息(nickname和密码pwd)
  • 统计一个字符串中每个单词出现的次数。比如:"how are you are you right",how=1,are=2,you=2,right=1
  • 观察下面代码,写出最终的打印结果
func main() {
	type Map map[string][]int
	m := make(Map)
	s := []int{1, 2}
	s = append(s, 3)
	fmt.Printf("%+v
", s)
	m["q1mi"] = s
	s = append(s[:1], s[2:]...)
	fmt.Printf("%+v
", s)
	fmt.Printf("%+v
", m["q1mi"])
}
原文地址:https://www.cnblogs.com/dabric/p/12337500.html