【转】Go maps in action
原文: https://blog.golang.org/go-maps-in-action
--------------------------------------------------------------------------------------------------------------
Introduction
One of the most useful data structures in computer science is the hash table. Many hash table implementations exist with varying properties, but in general they offer fast lookups, adds, and deletes. Go provides a built-in map type that implements a hash table.
Declaration and initialization
A Go map type looks like this:
map[KeyType]ValueType
where KeyType may be any type that is comparable (more on this later), and ValueTypemay be any type at all, including another map!
This variable m is a map of string keys to int values:
var m map[string]int
Map types are reference types, like pointers or slices, and so the value of m above is nil; it doesn't point to an initialized map. A nil map behaves like an empty map when reading, but attempts to write to a nil map will cause a runtime panic; don't do that. To initialize a map, use the built in make function:
m = make(map[string]int)
The make function allocates and initializes a hash map data structure and returns a map value that points to it. The specifics of that data structure are an implementation detail of the runtime and are not specified by the language itself. In this article we will focus on the use of maps, not their implementation.
Working with maps
Go provides a familiar syntax for working with maps. This statement sets the key "route" to the value 66:
m["route"] = 66
This statement retrieves the value stored under the key "route" and assigns it to a new variable i:
i := m["route"]
If the requested key doesn't exist, we get the value type's zero value. In this case the value type is int, so the zero value is 0:
j := m["root"]
// j == 0
The built in len function returns on the number of items in a map:
n := len(m)
The built in delete function removes an entry from the map:
delete(m, "route")
The delete function doesn't return anything, and will do nothing if the specified key doesn't exist.
A two-value assignment tests for the existence of a key:
i, ok := m["route"]
In this statement, the first value (i) is assigned the value stored under the key "route". If that key doesn't exist, iis the value type's zero value (0). The second value (ok) is a bool that is true if the key exists in the map, and false if not.
To test for a key without retrieving the value, use an underscore in place of the first value:
_, ok := m["route"]
To iterate over the contents of a map, use the range keyword:
for key, value := range m {
fmt.Println("Key:", key, "Value:", value)
}
To initialize a map with some data, use a map literal:
commits := map[string]int{
"rsc": 3711,
"r": 2138,
"gri": 1908,
"adg": 912,
}
The same syntax may be used to initialize an empty map, which is functionally identical to using the makefunction:
m = map[string]int{}
Exploiting zero values
It can be convenient that a map retrieval yields a zero value when the key is not present.
For instance, a map of boolean values can be used as a set-like data structure (recall that the zero value for the boolean type is false). This example traverses a linked list of Nodes and prints their values. It uses a map of Nodepointers to detect cycles in the list.
type Node struct {
Next *Node
Value interface{}
}
var first *Node
visited := make(map[*Node]bool)
for n := first; n != nil; n = n.Next {
if visited[n] {
fmt.Println("cycle detected")
break
}
visited[n] = true
fmt.Println(n.Value)
}
The expression visited[n] is true if n has been visited, or false if n is not present. There's no need to use the two-value form to test for the presence of n in the map; the zero value default does it for us.
Another instance of helpful zero values is a map of slices. Appending to a nil slice just allocates a new slice, so it's a one-liner to append a value to a map of slices; there's no need to check if the key exists. In the following example, the slice people is populated with Person values. Each Person has a Name and a slice of Likes. The example creates a map to associate each like with a slice of people that like it.
type Person struct {
Name string
Likes []string
}
var people []*Person
likes := make(map[string][]*Person)
for _, p := range people {
for _, l := range p.Likes {
likes[l] = append(likes[l], p)
}
}
To print a list of people who like cheese:
for _, p := range likes["cheese"] {
fmt.Println(p.Name, "likes cheese.")
}
To print the number of people who like bacon:
fmt.Println(len(likes["bacon"]), "people like bacon.")
Note that since both range and len treat a nil slice as a zero-length slice, these last two examples will work even if nobody likes cheese or bacon (however unlikely that may be).
Key types
As mentioned earlier, map keys may be of any type that is comparable. The language spec defines this precisely, but in short, comparable types are boolean, numeric, string, pointer, channel, and interface types, and structs or arrays that contain only those types. Notably absent from the list are slices, maps, and functions; these types cannot be compared using ==, and may not be used as map keys.
It's obvious that strings, ints, and other basic types should be available as map keys, but perhaps unexpected are struct keys. Struct can be used to key data by multiple dimensions. For example, this map of maps could be used to tally web page hits by country:
hits := make(map[string]map[string]int)
This is map of string to (map of string to int). Each key of the outer map is the path to a web page with its own inner map. Each inner map key is a two-letter country code. This expression retrieves the number of times an Australian has loaded the documentation page:
n := hits["/doc/"]["au"]
Unfortunately, this approach becomes unwieldy when adding data, as for any given outer key you must check if the inner map exists, and create it if needed:
func add(m map[string]map[string]int, path, country string) {
mm, ok := m[path]
if !ok {
mm = make(map[string]int)
m[path] = mm
}
mm[country]++
}
add(hits, "/doc/", "au")
On the other hand, a design that uses a single map with a struct key does away with all that complexity:
type Key struct {
Path, Country string
}
hits := make(map[Key]int)
When an Vietnamese person visits the home page, incrementing (and possibly creating) the appropriate counter is a one-liner:
hits[Key{"/", "vn"}]++
And it's similarly straightforward to see how many Swiss people have read the spec:
n := hits[Key{"/ref/spec", "ch"}]
Concurrency
Maps are not safe for concurrent use: it's not defined what happens when you read and write to them simultaneously. If you need to read from and write to a map from concurrently executing goroutines, the accesses must be mediated by some kind of synchronization mechanism. One common way to protect maps is with sync.RWMutex.
This statement declares a counter variable that is an anonymous struct containing a map and an embedded sync.RWMutex.
var counter = struct{
sync.RWMutex
m map[string]int
}{m: make(map[string]int)}
To read from the counter, take the read lock:
counter.RLock()
n := counter.m["some_key"]
counter.RUnlock()
fmt.Println("some_key:", n)
To write to the counter, take the write lock:
counter.Lock()
counter.m["some_key"]++
counter.Unlock()
Iteration order
When iterating over a map with a range loop, the iteration order is not specified and is not guaranteed to be the same from one iteration to the next. Since the release of Go 1.0, the runtime has randomized map iteration order. Programmers had begun to rely on the stable iteration order of early versions of Go, which varied between implementations, leading to portability bugs. If you require a stable iteration order you must maintain a separate data structure that specifies that order. This example uses a separate sorted slice of keys to print a map[int]string in key order:
import "sort" var m map[int]string
var keys []int
for k := range m {
keys = append(keys, k)
}
sort.Ints(keys)
for _, k := range keys {
fmt.Println("Key:", k, "Value:", m[k])
}
By Andrew Gerrand
【转】Go maps in action的更多相关文章
- 【UI插件】简单的日历插件(下)—— 学习MVC思想
前言 我们上次写了一个简单的日历插件,但是只是一个半成品,而且做完后发现一些问题,于是我们今天尝试来解决这些问题 PS:距离上次貌似很久了 上次,我们大概遇到哪些问题呢: ① 既然想做一套UI库,那么 ...
- Thinkphp源码分析系列(四)–Dispatcher类
下面我们来分析一下Thinkphp中的url解析和路由调度类.此类主要功能是 // +--------------------------------------------------------- ...
- 【iScroll源码学习03】iScroll事件机制与滚动条的实现
前言 想不到又到周末了,周末的时间要抓紧学习才行,前几天我们学习了iScroll几点基础知识: 1. [iScroll源码学习02]分解iScroll三个核心事件点 2. [iScroll源码学习01 ...
- 【再探backbone 01】模型-Model
前言 点保存时候不注意发出来了,有需要的朋友将就看吧,还在更新...... 几个月前学习了一下backbone,这段时间也用了下,感觉之前对backbone的学习很是基础,前几天有个园友问我如何将路由 ...
- Backbone事件模块源码分析
事件模块Backbone.Events在Backbone中占有十分重要的位置,其他模块Model,Collection,View所有事件模块都依赖它.通过继承Events的方法来实现事件的管理,可以说 ...
- Backbone笔记(续)
Backbone Bockbone 总览 Backbone 与 MVC 模式:解决某一类问题的通用方案 - 套路 MVC:一种架构模式,解耦代码,分离关注点 M(Model) - 数据模型 V(Vie ...
- BACKBONE源代码解析
//2014.11// Backbone.js 1.0.0 // (c) 2010-2013 Jeremy Ashkenas, DocumentCloud Inc. // Backbone may b ...
- Backbone Events 源码笔记
用了backbone一段时间了,做一些笔记和总结,看的源码是1.12 backbone有events,model,collection,histoty,router,view这些模块,其中events ...
- The Go Programming Language. Notes.
Contents Tutorial Hello, World Command-Line Arguments Finding Duplicate Lines A Web Server Loose End ...
随机推荐
- Loadrunner脚本读取 XMl 文件
Loadrunner脚本读取 XMl 文件 性能测试工程师要懂代码么?答案是必须的,好多测试员认为在 loadrunner 中编写脚本很难很牛 X . 好多人认为 loadrunner 只支持 C 语 ...
- UVALive - 7042 The Problem to Make You Happy 博弈
题目大意:给你一个有向图, Bob 和 Alice 在做游戏,每轮他们走一步,当Bob 和 Alice在同一个点或者 Bob无路可走,Bob输,否则Alice输. 思路:因为在Bob赢的时候存在有环的 ...
- Power BI连接至Mogo Altas Connector For BI
我需要使用Power BI连接至Connector For BI ,现在Connect For BI存放在Mongo Atlas中,详细的来自于官方文档,https://docs.atlas.mong ...
- 转:GitHub 万星推荐成长技术清单
转:http://www.4hou.com/info/news/7061.html 最近两天,在reddit安全板块和Twitter上有个GitHub项目很火,叫“Awesome Hacking”. ...
- 19. Remove Nth Node From End of List【Medium】【删除单链表倒数第n个结点】
Given a linked list, remove the n-th node from the end of list and return its head. Example: Given l ...
- Pythonic和语法糖
Pythonic就是以Python的方式写出简洁优美的代码. 用Python独有的语法写Python语言. 知乎:https://www.zhihu.com/question/20244565 某博客 ...
- JSP2 自定义标签
实现步骤 实现自定义标签的处理类继承javax.servlet.jsp.tagext.SimpleTagSupport,并重写doTag方法 建立标签库配置文件 在jsp中使用自定义标签 一个简单的标 ...
- 洛谷——P2239 螺旋矩阵
P2239 螺旋矩阵 题目描述 一个n行n列的螺旋矩阵可由如下方法生成: 从矩阵的左上角(第1行第1列)出发,初始时向右移动:如果前方是未曾经过的格子,则继续前进,否则右转:重复上述操作直至经过矩阵中 ...
- RabbitMQ (十五) 镜像集群 + HAProxy1.7.8 负载均衡
RabbitMQ 默认的集群模式,也就是普通模式,最大的问题就在于存储队列完整数据的节点一旦宕机, 如果是非持久化队列,则消息丢失;如果是持久化队列+持久化消息,则必须等该节点恢复. 所以后来 Rab ...
- luogu P2254 [NOI2005]瑰丽华尔兹
题目链接 luogu P2254 [NOI2005]瑰丽华尔兹 题解 为什么我我我不放放放bzoj的链接呢? 因为打的暴力啊,然后bzojT了呀QAQQQQQ(逃 然后luogu竟然过了呀呀呀 dp[ ...