go语言基础-map技巧
当一个map变量被创建后,你可以指定map的容量,但是不可以在map上使用cap()方法
代码示例:
package main
func main() {
m := make(map[string]int, 99)
cap(m) //error
}
command-line-arguments
./hello.go:5: invalid argument m (type map[string]int) for cap
先来看一下go的内置函数cap与map:
cap: 返回的是数组切片分配的空间大小, 根本不能用于map
make: 用于slice,map,和channel的初始化
要获取map的容量,可以用len函数。
关于map的cap问题
是不是只有连续内存空间的才有cap, map不是连续的底层空间, 所以map没有cap一说
map 必须用make进行初始化后才能使用,否则报错.
var temp = make(map[string]struct{})
或 temp := map[string]struct{}{}map 空判断
if _, ok := map[key]; ok { //存在 }
Golang workaround for cannot assign to struct field in map ?
- golang 不能直接修改map[]struct 的值,如果想修改 必须用指针形式
Yesterday, I was working one of the Kompose issue, and I was working on map of string to struct, while iterating over a map I wanted to change elements of struct, so I tried similar to this, package main import "fmt" type Animal struct { count int } func main() { m := map[string]Animal{"cat": Animal{2}, "dog": Animal{3}, "mouse": Animal{5}} fmt.Println(m) m["dog"].count = 4 fmt.Println(m) } so I got this error, tmp/sandbox728133053/main.go:12: cannot assign to struct field m["dog"].count in map After googling for some time, I found this solution and I tried & it worked as below: package main import "fmt" type Animal struct { count int } func main() { m := map[string]Animal{"cat": Animal{2}, "dog": Animal{3}, "mouse": Animal{5}} fmt.Println(m) var x = m["dog"] x.count = 4 m["dog"] = x fmt.Println(m) } I found one more way to do this by storing pointers as shown below: package main import "fmt" type Animal struct { count int } func main() { m := map[string]*Animal{"cat": &Animal{2}, "dog": &Animal{3}, "mouse": &Animal{5}} fmt.Printf("%#v\n",m["dog"]) m["dog"].count = 4 fmt.Printf("%#v", m["dog"]) }
- golang 不能直接修改map[]struct 的值,如果想修改 必须用指针形式
利用空struct{}对slice去重
func main() { s := []string{"hello", "world", "hello", "golang", "hello", "ruby", "php", "java"} fmt.Println(removeDuplicateElement(s)) } func removeDuplicateElement(addrs []string) []string { result := make([]string, 0, len(addrs)) temp := map[string]struct{}{} for _, item := range addrs { if _, ok := temp[item]; !ok { temp[item] = struct{}{} result = append(result, item) } } return result }
func (s *Schema) hasDuplicateValue(values []string) bool { l := len(values) tmp := map[string]bool{} for _, v := range values { tmp[v] = true } return l != len(tmp) }
sync.Map
// sync.Map的实现有几个优化点,这里先列出来,我们后面慢慢分析。
// 空间换时间。 通过冗余的两个数据结构(read、dirty),实现加锁对性能的影响。
// 使用只读数据(read),避免读写冲突。
// 动态调整,miss次数多了之后,将dirty数据提升为read。
// double-checking。
// 延迟删除。 删除一个键值只是打标记,只有在提升dirty的时候才清理删除的数据。
// 优先从read读取、更新、删除,因为对read的读取不需要锁。
const N = 60000
func main() {
var m sync.Map
var g errgroup.Group
for i := 0; i < N; i++ {
g.Go(func() error {
m.Store(rand.Int(), rand.Int())
return nil
})
}
g.Wait()
m.Delete(rand.Int())
m.Load(rand.Int())
m.Store("abc", "1")
res, err := m.Load("abc")
fmt.Println(res, err)
var iter = func(k, v interface{}) bool {
fmt.Println(k, v)
return true
}
m.Range(iter)
}
错误信息cannot assign to struct field m["foo"].x in map
注意: Golang中是无法修改map中的成员变量,修改成指针就行了
在开始代码设计的时候想要将原struct中的成员变量进行修改或者替换。
代码示例如下
package main
import "fmt"
var m = map[string]struct{ x, y int } {
"foo": {2, 3}
}
func main() {
m["foo"].x = 4
fmt.Printf("result is : %+v", m)
}
本以为这个会将 m[“foo”] 中的 x 替换成 4, 从而打印出来的效果是
result is : map[foo:{x:4 y:3}]
然而,并不是的,这段代码在保存后编译时提示
cannot assign to struct field m["foo"].x in map
这就尴尬了,无法在已经存在的key的节点中修改值,这是为什么?
m中已经存在”foo”这个节点了啊,
然后就去google搜了下,然后看到在github上有人提到这个问题, 问题地址 issue-3117
ianlancetaylor 回答给出了一个比较能理解的解释。
简单来说就是map不是一个并发安全的结构,所以,并不能修改他在结构体中的值。
这如果目前的形式不能修改的话,就面临两种选择,
1.修改原来的设计;
2.想办法让map中的成员变量可以修改,
因为懒得该这个结构体,就选择了方法2,
但是不支持这种方式传递值,应该如何进行修改现在已经存在在struct中的map的成员变量呢?
热心的网友们倒是提供了一种方式,示例如下:
package main
import "fmt"
var m = map[string]struct{ x, y int } {
"foo": {2, 3}
}
func main() {
tmp := m["foo"]
tmp.x = 4
m["foo"] = tmp
fmt.Printf("result is : %+v", m)
}
果然和预期结果一致,不过,总是觉得有点怪怪的,
既然是使用了类似临时空间的方式,那我们用地址引用传值不也是一样的么...
于是,我们就使用了另外一种方式来处理这个东西,
示例如下:
package main
import "fmt"
var m = map[string]*struct{ x, y int } {
"foo": &{2, 3}
}
func main() {
m["foo"].x = 4
fmt.Println("result is : %+v \n", m)
fmt.Println("m's node is : %+v \n", *m["foo"])
}
最后的展示结果为:
result is : map[foo:0xc42000cff0]
m's node is : {4, 3}
多亏了经过这么一番折腾,我知道了,下次要是想在struct中的map里面改变成员变量,就直接用地址吧。