go map基本操作-增删改查

目录

  • map中的key的数据类型
    • key的几种数据类型举例
    • 判断某个数据类型是否可以作为key的数据类型
  • map基本操作
    • map创建
    • map增删改查
      • 查找
      • 增加,修改
      • 删除
    • map遍历
      • map遍历易错点举例 - 保存临时变量的地址
  • map其他操作
    • 获取map中元素的个数

  • go中的map是hash表的一个引用,类型写为:map[key]value,其中的key, value分别对应一种数据类型,如map[string]string
  • 要求所有的key的数据类型相同,所有value数据类型相同(注:key与value可以有不同的数据类型)

map中的key的数据类型

  • map中的每个key在keys的集合中是唯一的,而且需要支持 == or != 操作
  • key的常用类型:int, rune, string, 结构体(每个元素需要支持 == or != 操作), 指针, 基于这些类型自定义的类型

float32/64 类型从语法上可以作为key类型,但是实际一般不作为key,因为其类型有误差

key的几种数据类型举例

下面使用具体例子介绍几种可以作为key的数据类型以及不能作为key的数据类型

  • string类型可以作为key的数据类型,因为其支持 == 比较操作,如变量 m0
  • interface{}类型可以作为key,但是需要加入的key的类型是可以比较的据类型, 如果加入key的元素的变量类型是不能比较的,会发生panix,如变量 m1
  • 数组支持比较,可以为key的数据类型,如变量 m2
  • slice 不可以, 因为其不支持 == != 操作,不可以作为map key的数据类型(准确说slice类型只能与nil比较,其他的都不可以),如变量 m3
  • 如果struct中的所有元素都支持比较操作,那么这个struct类型可以作为key的数据类型,如变量 m4
  • 如果struct中的某个元素不支持比较操作,那么这个struct类型不可以作为key的数据类型,如变量m5
    // m0 string
    {
        var m0 map[string]string // 定义map类型变量m0,key的类型为string,value的类型string
        fmt.Println(m0)          // 输出: map[]
    }

    // m1 interface{}
    {
        var m1 map[interface{}]string
        m1 = make(map[interface{}]string)
        //m1[[]byte("k2")]="v2" // panic: runtime error: hash of unhashable type []uint8
        m1[123] = "123"
        m1[12.3] = "123"
        fmt.Println(m1) // map[123:123 12.3:123]
    }

    // m2 数组
    {
        a2 := [3]int{1, 2, 3}
        var m2 map[[3]int]string
        m2 = make(map[[3]int]string)
        m2[a2] = "m2"
        fmt.Println(m2) // map[[1 2 3]:m2]
    }

    // m3 slice
    {
        //var m3 map[[]byte]string // 报错: invalid map key type []byte
        //fmt.Println(m3)
    }

    // m4 可以,book1里面的元素都是支持== !=
    {
        type book1 struct {
            name string
        }
        var m4 map[book1]string
        fmt.Println(m4) // 输出: map[]
    }

    // m5 不可以, text元素类型为[]byte, 不满足key的要求
    {
        // type book2 struct {
        //  name string
        //  text []byte //没有这个就可以
        // }
        //var m5 map[book2]string //invalid map key type book2
        //fmt.Println(m5)
    }

如何判断某个数据类型是否可以作为map key的数据类型

  • 直接建立一个map,然后运行,如果报错说明不支持;但是interface{}特殊,只有在加入变量的时候才可以判断是否支持,因为在定义的时候,并不知道实际放入key的数据的具体类型
  • 定义两个某种类型的变量,使用 == 比较一下, 如果不报错说明支持,如下面的例子 两个string类型的变量进行比较操作,返回true说明支持; 两个slice变量进行比较操作报错,说明不支持
    var s1 = "ss"
    var s2 = "ss"
    fmt.Println(s1 == s2) // 输出: true

    // var b1, b2 []byte
    // fmt.Println(b1 == b2) // 报错: invalid operation: b1 == b2 (slice can only be compared to nil)

map基本操作

map创建

  • 两种创建的方式:一是通过字面值;二是通过make函数
    // 1 字面值
    {
        m1 := map[string]string{
            "m1": "v1", // 定义时指定的初始key/value, 后面可以继续添加
        }
        _ = m1

    }

    // 2 使用make函数
    {
        m2 := make(map[string]string) // 创建时,里面不含元素,元素都需要后续添加
        m2["m2"] = "v2"               // 添加元素
        _ = m2

    }

    // 使用两种方法定义一个空的map
    {
        m3 := map[string]string{}
        m4 := make(map[string]string)
        _ = m3
        _ = m4
    }

map增删改查

查找

    // 创建
    m := map[string]string{
        "a": "va",
        "b": "vb",
    }

    // 查
    {
        // v := m[k] // 从m中取键k对应的值给v,如果k在m中不存在,则将value类型的零值赋值给v
        // v, ok := m[k] // 从m中取键k对应的值给v,如果k存在,ok=true,如果k不存在,将value类型的零值赋值给v同时ok=false
        
// 查1 - 元素不存在
        v1 := m["x"] //
        v2, ok2 := m["x"]
        fmt.Printf("%#v %#v %#v\n", v1, v2, ok2) // "" "" false

        // 查2 - 元素存在
        v3 := m["a"]
        v4, ok4 := m["a"]
        fmt.Printf("%#v %#v %#v\n", v3, v4, ok4) //"va" "va" true
    }

增加,修改

  • 当key不存在map中的时候为增加操作,当key存在于map的时候修改操作,会使用新的value覆盖掉旧的value
    // 创建
    m := map[string]string{
        "a": "va",
        "b": "vb",
    }

    // 增加,修改
    {
        // k不存在为增加,k存在为修改
        m["c"] = ""                        // 增加操作
        m["c"] = "11"                      // 重复增加(key相同),使用新的值覆盖
        fmt.Printf("%#v %#v\n", m, len(m)) // map[string]string{"a":"va", "b":"vb", "c":"11"} 3
    }
  • 两种方式可以判断map中是否存在某个key
    • 方法一 使用key获取value,然后与value数据类型的零值进行比较,如果返回了类型零值说明不存在这个key。如下面的方式一,这种方式比较简单,但是会存在一个小的瑕疵,比如key真的存在于map中,但是value的值是类型对应的零值的时候,会出现判断失误,比如 m["c"]="",string类型的零值为 "", 这种情况 key="c"是存在的,但是使用这种方式会判断为"c"不存在于map中
    • 方法二 返回值多加一个变量来判断是否存在。返回的第一个值是map中的key对应的value值,第二个是一个bool型变量,如果返回true说明变量存在于map中,如果false说明变量不存在map中
    // 创建
    m := map[string]string{
        "a": "va",
        "b": "vb",
    }

    // 一 判断key是否存在, 简版,有瑕疵
    {
        v := m["a"]
        fmt.Println(v)
    }

    // 二 判断key是否存在,准确
    {
        v, exist := m["a"]
        fmt.Println(v)
        fmt.Println(exist)
    }

删除

    // 创建
    m := map[string]string{
        "a": "va",
        "b": "vb",
    }

    // 删, 使用内置函数删除k/v对
    {
        // delete(m, k) 将k以及k对应的v从m中删掉;如果k不在m中,不执行任何操作
        delete(m, "x")                     // 删除不存在的key,原m不影响
        delete(m, "a")                     // 删除存在的key
        fmt.Printf("%#v %#v\n", m, len(m)) // map[string]string{"b":"vb", "c":"11"} 2
        delete(m, "a")                     // 重复删除不报错,m无影响
        fmt.Printf("%#v %#v\n", m, len(m)) /// map[string]string{"b":"vb", "c":"11"} 2
    }

map遍历

  • 遍历的顺序是随机的
  • 使用for range遍历的时候,k,v使用的同一块内存,这也是容易出现错误的地方
    // 创建
    m := map[string]string{
        "a": "va",
        "b": "vb",
    }

    // 遍历
    {
        for k, v := range m {
            fmt.Printf("k:[%v].v:[%v]\n", k, v) // 输出k,v值
        }
    }
    // k:[a].v:[va]
    // k:[b].v:[vb]

map遍历易错点举例 - 保存临时变量的地址

由于遍历的时候,遍历k, v使用的同一块地址,同时这块地址是临时分配的。虽然地址没有变化,但内容在一直变化。在下面的例子中,当遍历完成后,v的内容是map遍历时最后遍历的元素的值(map遍历无序,每次不确定哪个元素是最后一个元素)。当程序将v的地址放入到slice中的时候,slice在不断地v的地址插入,由于v一直是那块地址,因此slice中的每个元素记录的都是v的地址。因此当打印slice中的内容的时候,都是同一个值

    // 创建
    m := map[string]string{
        "a": "va",
        "b": "vb",
    }

    // 保存临时变量的地址
    {
        var bs []*string
        for k, v := range m {
            fmt.Printf("k:[%p].v:[%p]\n", &k, &v) // 这里的输出可以看到,k一直使用同一块内存,v也是这个状况
            bs = append(bs, &v)                   // 对v取了地址
        }
        // 输出
        // k:[0xc00004a1c0].v:[0xc00004a1d0]
        // k:[0xc00004a1c0].v:[0xc00004a1d0]

        for _, b := range bs {
            fmt.Println(*b) // 输出都是1或者都是2
        }
        // 输出
        // vb
        // vb
    }

map其他操作

  • 获取map中元素的个数
    // 创建
    m := map[string]string{
        "a": "va",
        "b": "vb",
    }
    fmt.Println(len(m)) // len(m) 获得m中key/value对的个数
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,377评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,390评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,967评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,344评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,441评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,492评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,497评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,274评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,732评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,008评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,184评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,837评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,520评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,156评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,407评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,056评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,074评论 2 352

推荐阅读更多精彩内容