2.5 Go语言基础之map

Go语言中提供的映射关系容器为map

  1. Go中内置类型,其内部使用散列表(hash)实现,为引用类型

  2. 无序键值对(key-value)集合,通过key(类似索引)快速检索数据

  3. 必须初始化才能使用

一、map

1.1 map是什么?

Map是一种数据结构,是一个集合,用于存储一系列无序的键值对。

基于键存储的,可以快速快速检索数据,键指向与该键关联的值

1.2 map的内部实现

Map存储的是什么?Map存储的是无序的键值对集合。

Map基于什么?基于散列表(hash表),故每次迭代Map,打印的key和value是无序的,每次迭代不一样

Map散列表特点是?包含一组桶,每次存储和查找键值对的时候,都要先选择一个桶。

如何选择桶?把指定的键传给散列函数,就可索引到相应的桶,进而找到对应的键值。

这种方式的好处是?存储的数据越多,索引分布越均匀,所以我们访问键值对的速度也就越快

1.3 map声明和定义

Go语言中 map的定义语法如下:

map[KeyType]ValueType

其中,

  • KeyType:表示键的类型。
  • ValueType:表示键对应的值的类型。

例子:

//var a map[key的类型]value类型
var a map[string]int
var b map[int]string
var c map[float32]string

注意:

1.声明是不会分配内存的,需要make初始化

2.map必须初始化才能使用,否则panic。

3.map中声明value是什么类型,就存什么类型,混合类型自身不支持(Go是强类型语言),interfice支持存混合类型数据。

map类型的变量未初始化前默认为nil,所以需要使用make分配map内存,然后才能使用,不然会panic(异常)

实例如下:

实例1-1

package main

import (
    "fmt"
)

func main() {
    var a map[string]int
    if a == nil {  //map未初始化,其初始默认为nil
        fmt.Println("map is nil. Going to make one.")
        a = make(map[string]int)
    }
}

执行结果如下:

img

另外也验证一下不初始化跑出panic

实例如下:

实例1-2

package main

import (
    "fmt"
)

func main() {
    var user map[string]int
    user["abc"] = 38
    fmt.Println(user)
}

执行结果如下:

img

已经抛出了panic,所以必须要初始化。

1.4 map初始化

1.4.1 方法1 make

map类型的变量默认初始值为nil,需要使用make()函数来分配内存。语法为:

make(map[KeyType]ValueType, [cap])

其中cap表示map的容量,该参数虽然不是必须的,但是我们应该在初始化map的时候就为其指定一个合适的容量。

实例:

package main

import (
    "fmt"
)

func main() {
    var user map[string]int = make(map[string]int, 5000) //初始化时可以指定容量也可以不指定,指定的话可以提升性能
    //user := make(map[string]int)  //也可以写成这样,多种方式
    user["abc"] = 38
    fmt.Println(user)
}

执行结果:

img

1.4.2 方法2 声明时进行初始化(借助常量)

m:= map[string]int{"张三":21}
m2:= map[string]int{"张三":21,"李四":22}
m3:= map[string]int{}        //空map

实例:

package main

import (
    "fmt"
)

func main() {
    a := map[string]int{
        "steve": 12000,
        "jamie": 15000,
    }
    a["mike"] = 9000
    fmt.Println("a map contents:", a)
}

执行结果:

img

1.6 map扩容

map扩容实际上类似切片扩容就是:

map本来的容量是4,现在容量不够了,map内部自动扩容,比如说扩容到8,其在底层的机制就是将旧的内存地址中的4个元素拷贝到新到容量为8的内存地址中,然后再继续接收新元素并使用。

所以说:在初始化时,如果我们知道map大概有多少元素时,可以初始化时指定,这样可以在一定程度上提升性能(频繁扩容影响性能)

1.7 如何访问map中的元素

通过key访问map中的元素

package main

import "fmt"

func main() {
    a := map[string]int{
        "steve": 12000,
        "jamie": 15000,
    }
    a["mike"] = 9000
    b := "jamie"
    fmt.Println("Salary of", b, "is", a[b])
}

执行结果:

img

1.8 map添加键值对

map中的数据都是成对出现的,map的基本使用示例代码如下:

func main() {
	scoreMap := make(map[string]int, 8)
	scoreMap["张三"] = 90
	scoreMap["小明"] = 100
	fmt.Println(scoreMap)
	fmt.Println(scoreMap["小明"])
	fmt.Printf("type of a:%T
", scoreMap)
}

输出:

map[小明:100 张三:90]
100
type of a:map[string]int

map也支持在声明的时候填充元素,例如:

func main() {
	userInfo := map[string]string{
		"username": "哈登",
		"password": "123456",
	}
	fmt.Println(userInfo) //
}

1.9 判断某个键(key)是否存在

Go语言中有个判断map中键是否存在的特殊写法,

格式如下:

value, ok := map[key]

相当于做一个白名单,去判断map中指定key是否存在

注意:ok仅仅是个变量,可以随便命名

举个例子:

func main() {
	scoreMap := make(map[string]int)
	scoreMap["张三"] = 90
	scoreMap["小明"] = 100
	// 如果key存在ok为true,v为对应的值;不存在ok为false,v为值类型的零值
	v, ok := scoreMap["张三"]
	if ok {
		fmt.Println(v)
	} else {
		fmt.Println("查无此人")
	}
}

实例2:

用户白名单

package main

import (
    "fmt"
)

var whiteUser map[int]bool = map[int]bool{
    32323: true,
    32011: true,
    10222: true,
}

func isWhiteUser(userId int) bool {
    _, ok := whiteUser[userId] //这里不需要返回value,所以直接_忽略
    return ok
}

func main() {
    userId := 100021
    if isWhiteUser(userId) {
        fmt.Printf("is white user:%v
", userId)
    } else {
        fmt.Printf("is normal user:%v
", userId)
    }
}

执行结果:

1556273898168

1.10 map的遍历

Go语言中使用for range遍历map。

range返回key value并赋值给变量,结合数组、切片遍历也是for range,其实数组及切片就是个特殊map。

func main() {
	scoreMap := make(map[string]int)
	scoreMap["张三"] = 90
	scoreMap["小明"] = 100
	scoreMap["娜扎"] = 60
	for k, v := range scoreMap {
		fmt.Println(k, v)
	}
}

但我们只想遍历key的时候,可以按下面的写法:

package main

import "fmt"

func main() {
	scoreMap := map[string]int{
		"张三": 90,
		"小明": 100,
		"张飞": 80,
	}
	for k := range scoreMap {
		fmt.Println(k)
	}
}

注意: 遍历map时输出的元素顺序与填充顺序无关。

1.11 使用delete()函数删除键值对

使用delete()内建函数从map中删除一组键值对,delete()函数的格式如下:

delete(map, key)

其中, map:表示要删除键值对的map ,key:表示要删除的键值对的键

示例代码如下:

func main(){
	scoreMap := make(map[string]int)
	scoreMap["张三"] = 90
	scoreMap["小明"] = 100
	scoreMap["娜扎"] = 60
	delete(scoreMap, "小明")//将小明:100从map中删除
	for k,v := range scoreMap{
		fmt.Println(k, v)
	}
}

实例2:

package main

import (
    "fmt"
)

func main() {
    a := map[string]int{
        "steve": 12000,
        "jamie": 15000,
    }
    a["mike"] = 9000
    fmt.Println("map before deletion", a)
    delete(a, "steve")
    fmt.Println("map after deletion", a)
}

执行结果:

img

1.12 map的长度

借助len函数

代码示例:

package main

import (
    "fmt"
)

func main() {
    a := map[string]int{
        "steve": 12000,
        "jamie": 15000,
    }
    a["mike"] = 9000
    fmt.Println("length is", len(a))
}

执行结果:

img

1.13 按照指定顺序遍历map(扩展)

func main() {
	rand.Seed(time.Now().UnixNano()) //初始化随机数种子

	var scoreMap = make(map[string]int, 200)

	for i := 0; i < 100; i++ {
		key := fmt.Sprintf("stu%02d", i) //生成stu开头的字符串
		value := rand.Intn(100)          //生成0~99的随机整数
		scoreMap[key] = value
	}
	//取出map中的所有key存入切片keys
	var keys = make([]string, 0, 200)
	for key := range scoreMap {
		keys = append(keys, key)
	}
	//对切片进行排序
	sort.Strings(keys)
	//按照排序后的key遍历map
	for _, key := range keys {
		fmt.Println(key, scoreMap[key])
	}
}

1.14 map是引用类型

通过下面这个例子验证map是引用类型

代码示例如下:

package main

import (
    "fmt"
)

func main() {
    a := map[string]int{
        "steve": 12000,
        "jamie": 15000,
    }
    a["mike"] = 9000
    fmt.Println("origin map", a)
    b := a
    b["mike"] = 18000
    fmt.Println("a map changed", a)
}

执行结果如下:

1556277253957

解释:

可以发现a为map,将a赋值给b,b也是map,map b做了修改,此时打印a,map a也发生了变化,这证明map是引用类型

1.7 元素为map类型的切片

下面的代码演示了切片中的元素为map类型时的操作:

实例1:

func main() {
	var mapSlice = make([]map[string]string, 3)
	for index, value := range mapSlice {
		fmt.Printf("index:%d value:%v
", index, value)
	}
	fmt.Println("after init")
	// 对切片中的map元素进行初始化
	mapSlice[0] = make(map[string]string, 10)
	mapSlice[0]["name"] = "哈登"
	mapSlice[0]["password"] = "123456"
	mapSlice[0]["address"] = "休斯顿"
	for index, value := range mapSlice {
		fmt.Printf("index:%d value:%v
", index, value)
	}
}

实例2:

package main

import (
    "fmt"
)

func main() {
    var mapSlice []map[string]int
    mapSlice = make([]map[string]int, 5) //此时是一个未初始化的map类型切片,所以需要先将切片初始化
    fmt.Println("before map init")
    for index, value := range mapSlice {
        fmt.Printf("index:%d value:%v
", index, value)
    }
    fmt.Println()
    mapSlice[0] = make(map[string]int, 10)  //要使用map,需要将map进行初始化。
    mapSlice[0]["a"] = 1000
    mapSlice[0]["b"] = 2000
    mapSlice[0]["c"] = 3000
    mapSlice[0]["d"] = 4000
    mapSlice[0]["e"] = 5000
    fmt.Println("after map init")
    for index, value := range mapSlice {
        fmt.Printf("index:%d value:%v
", index, value)
    }
}

执行结果:

img

1.8 值为切片类型的map

下面的代码演示了map中值为切片类型的操作:

package main

import "fmt"

func main() {
	var sliceMap = make(map[string][]string, 3)
	fmt.Println(sliceMap)
	fmt.Println("after init")
	key := "中国"
	value, ok := sliceMap[key]
	if !ok {
		value = make([]string, 0, 2)
	}
	value = append(value, "北京", "上海")
	sliceMap[key] = value
	fmt.Println(sliceMap)
}

执行结果:

1556622719501

原文地址:https://www.cnblogs.com/forever521Lee/p/10797727.html