• 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

  • 相关阅读:
    一些程序员必备的英语词汇及释义
    ETL工具Talend最佳实践
    spark-submit使用yarn cluster模式时如何获取applicationId?
    On-heap vs Off-heap 堆内内存与堆外内存
    【Kail 学习笔记】kali信息搜集工具之IKE-Scan
    【Kail 学习笔记】kali信息搜集工具之Sparta(斯巴达)
    渗透常用命令
    渗透测试中常用WINDOWS命令
    Jvoke:Java环境下调用系统命令
    SpringCloud以及Nacos服务注册IP选择问题
  • 原文地址:https://www.cnblogs.com/forever521Lee/p/10797727.html
Copyright © 2020-2023  润新知