Go语言基础之map

狂风中的少年 提交于 2020-02-20 21:59:08

map概述

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

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

map的声明

基本语法

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。

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是无序的

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"])

map的增删改查操作

map增加和更新

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

删除

删除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])
}

查找

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))
}

遍历

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\n", 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("\tkey2=%v value2=%v\n", 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
}

map切片

基本介绍

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

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]]
}

map排序

基本介绍

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

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

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])
    }
}

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}]
}

练习

  • 使用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\n", s)
    m["q1mi"] = s
    s = append(s[:1], s[2:]...)
    fmt.Printf("%+v\n", s)
    fmt.Printf("%+v\n", m["q1mi"])
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!