go语言之map

 1.map认识       

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

        在Go语言中,一个map就是一个哈希表的引用,map类型可以写为map[K]V,其中K和V分别对应的key和value。map中所有的key都是相同的类型,所有的value也是相同的类型,但是key和value之间可以是不同的数据类型。其中K对应的key必须是支持==比较运算符的数据类型,所以map可以通过测试key是否相等来判断是否已经存在。

 2.map的创建       

        内置的make函数可以创建一个map:

ages := make(map[string]int) // mapping from strings to ints

        我们也可以用map字面值的语法创建map,同时还可以指定一些最初的key/value:

package main

import "fmt"

func main() {
	ages := map[string]int{
		"alice":   31,
		"charlie": 34,
	}
	fmt.Println(ages)
}

map[alice:31 charlie:34]

        也可以用下列来创建map:

package main

import "fmt"

func main() {
	ages := make(map[string]int)
	ages["alice"] = 31
	ages["charlie"] = 34
	fmt.Println(ages)
}

map[alice:31 charlie:34]

        因此,另一种创建空的map的表达式是 map[string]int{} 。

3.map的引用

        Map中的元素通过key对应的下标语法访问:

package main

import "fmt"

func main() {
	ages := make(map[string]int)
	ages["alice"] = 31
	ages["charlie"] = 34
	fmt.Println(ages["alice"])
}

31

        我们也可以用内置的delete函数可以删除元素:

package main

import "fmt"

func main() {
	ages := make(map[string]int)
	ages["alice"] = 31
	ages["charlie"] = 34
	delete(ages, "alice")
	fmt.Println(ages["alice"])
	fmt.Println(ages)
}

0
map[charlie:34]

        所有这些操作是安全的,即使这些元素不在map中也没有关系;如果一个查找失败将返回 value类型对应的零值,例如,即使map中不存在“bob”下面的代码也可以正常工作,因为 ages["bob"]失败时将返回0。

        

package main

import "fmt"

func main() {
	ages := make(map[string]int)
	ages["alice"] = 31
	ages["charlie"] = 34
	ages["bob"] = ages["bob"] + 1
	fmt.Println(ages)
}

map[alice:31 bob:1 charlie:34]

        而且 x += y 和 x++ 等简短赋值语法也可以用在map上,所以上面的代码可以改写成

ages["bob"] += 1

        更简单的写法

ages["bob"]++

        但是map中的元素并不是一个变量,因此我们不能对map的元素进行取址操作:

_ = &ages["bob"] // compile error: cannot take address of map element

        禁止对map元素取址的原因是map可能随着元素数量的增长而重新分配更大的内存空间,从而 可能导致之前的地址无效。要想遍历map中全部的key/value对的话,可以使用range风格的for循环实现,和之前的slice遍历语法类似。下面的迭代语句将在每次迭代时设置name和age变量,它们对应下一个键/值对:

package main

import "fmt"

func main() {
	ages := make(map[string]int)
	ages["alice"] = 31
	ages["charlie"] = 34
	ages["bob"] = ages["bob"] + 1
	fmt.Println(ages)
	for name, age := range ages {
		fmt.Printf("%s\t%d\n", name, age)
	}
}

map[alice:31 bob:1 charlie:34]
charlie	34
bob	1
alice	31

        Map的迭代顺序是不确定的,并且不同的哈希函数实现可能导致不同的遍历顺序。在实践中,遍历的顺序是随机的,每一次遍历的顺序都不相同。这是故意的,每次都使用随机的遍历顺序可以强制要求程序不会依赖具体的哈希函数实现。如果要按顺序遍历key/value对,我 们必须显式地对key进行排序,可以使用sort包的Strings函数对字符串slice进行排序。下面是 常见的处理方式:

package main

import (
	"fmt"
	"sort"
)

func main() {
	ages := make(map[string]int)
	ages["alice"] = 31
	ages["charlie"] = 34
	var names []string
	for name := range ages {
		names = append(names, name)
	}
	sort.Strings(names)
	for _, name := range names {
		fmt.Printf("%s\t%d\n", name, ages[name])
	}

}

alice	31
charlie	34

        因为我们一开始就知道names的最终大小,因此给slice分配一个合适的大小将会更有效。下 面的代码创建了一个空的slice,但是slice的容量刚好可以放下map中全部的key:

names := make([]string, 0, len(ages))

        在上面的第一个range循环中,我们只关心map中的key,所以我们忽略了第二个循环变量。 在第二个循环中,我们只关心names中的名字,所以我们使用“_”空白标识符来忽略第一个循 环变量,也就是迭代slice时的索引。 map类型的零值是nil,也就是没有引用任何哈希表。

        在向map存数据前必须先创建map。 通过key作为索引下标来访问map将产生一个value。如果key在map中是存在的,那么将得到 与key对应的value;如果key不存在,那么将得到value对应类型的零值,正如我们前面看到的 ages["bob"]那样。这个规则很实用,但是有时候可能需要知道对应的元素是否真的是在map 之中。例如,如果元素类型是一个数字,你可以需要区分一个已经存在的0,和不存在而返回 零值的0,可以像下面这样测试:

age, ok := ages["bob"]
if !ok { /* "bob" is not a key in this map; age == 0. */ }
或者
if age, ok := ages["bob"]; !ok { /* ... */ }

        在这种场景下,map的下标语法将产生两个值;第二个是一个布尔值,用于报告元素是否真 的存在。布尔变量一般命名为ok,特别适合马上用于if条件判断部分。 和slice一样,map之间也不能进行相等比较;唯一的例外是和nil进行比较。要判断两个map是 否包含相同的key和value,我们必须通过一个循环实现:

func equal(x, y map[string]int) bool {
    if len(x) != len(y) {
        return false
    }
    for k, xv := range x {
        if yv, ok := y[k]; !ok || yv != xv {
            return false
        }
    }
    return true
}

  • 20
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值