go map and slice 2021-10-08
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了go map and slice 2021-10-08相关的知识,希望对你有一定的参考价值。
参考技术Agolang是值传递,什么情况下都是值传递
那么,如果结构中不含指针,则直接赋值就是深度拷贝;
如果结构中含有指针(包括自定义指针,以及slice,map等使用了指针的内置类型),则数据源和拷贝之间对应指针会共同指向同一块内存,这时深度拷贝需要特别处理。因为值传递只是把指针拷贝了
map源码:
https://github.com/golang/go/blob/a7acf9af07bdc288129fa5756768b41f312d05f4/src/runtime/map.go
map最重要的两个结构体: hmap 和 bmap
其中 hmap 充当了哈希表中数组的角色, bmap充当了链表的角色。
其中,单个bucket是一个叫bmap的结构体.
Each bucket contains up to 8 key/elem pairs.
And the low-order bits of the hash are used to select a bucket. Each bucket contains a few high-order bits of each hash to distinguish the entries within a single bucket.
hash值的低位用来定位bucket,高位用来定位bucket内部的key
根据上面bmap的注释和 https://github.com/golang/go/blob/go1.13.8/src/cmd/compile/internal/gc/reflect.go ,
我们可以推出bmap的结构实际是
注意:在哈希桶中,键值之间并不是相邻排列的,而是键放在一起,值放在一起,来减少因为键值类型不同而产生的不必要的内存对齐
例如map[int64]int8,如果 key/elem/key/elem这样存放,那么int8类型的值就要padding 7个字节共56bits
更多可参考
https://zhuanlan.zhihu.com/p/406751292
https://studygolang.com/articles/32943
因此,slice、map作为参数传递给函数形参,在函数内部的改动会影响到原slice、map
go语言基础切片,map
目录
切片(Slice)
切片使用
package main
import "fmt"
func main()
//基于数组得到切片
a := [5]int11,22,33,44,55
b := a[1:4]
fmt.Println(b)
fmt.Printf("%T\\n", b)
//切片再次切片
c := b[0:len(b)]
fmt.Println(c)
fmt.Printf("%T\\n", c)
//make函数构造切片
d := make([]int, 5, 10)
fmt.Println(d)
fmt.Printf("%T\\n", d)
fmt.Println(len(d))//查看长度
fmt.Println(cap(d))//查看容量
Operation results:
[22 33 44]
[]int
[22 33 44]
[]int
[0 0 0 0 0]
[]int
5
10
切片内存申请
package main
import "fmt"
func main()
var a []int //未初始化没内存
var b = []int //初始化
c := make([]int, 0) //构造
if a == nil
fmt.Println("a == nil")
fmt.Println("a len =", len(a), "a cap =", cap(a))
if b == nil
fmt.Println("b == nil")
fmt.Println("b len =", len(b), "b cap =", cap(b))
if c == nil
fmt.Println("c == nil")
fmt.Println("c len =", len(c), "c cap =", cap(c))
Operation results:
a == nil
a len = 0 a cap = 0
b len = 0 b cap = 0
c len = 0 c cap = 0
切片赋值拷贝
package main
import "fmt"
func main()
a := make([]int ,3) //[0 0 0]
b := a
fmt.Println(a)
fmt.Println(b)
b[0] = 100
fmt.Println(a)
fmt.Println(b)
Operation results:
[0 0 0]
[0 0 0]
[100 0 0]
[100 0 0]
切片遍历
package main
import "fmt"
func main()
//切片的遍历
a := []int1, 2, 3, 4, 5
//第一列下标,第二列遍历出来的数据
for i := 0;i < len(a);i++
fmt.Println(i, a[i])
fmt.Println("///")
for index, value := range a
fmt.Println(index, value)
Operation results:
0 1
1 2
2 3
3 4
4 5
///
0 1
1 2
2 3
3 4
4 5
切片扩容
定义切片的时候最好一次性定好,避免动态扩容最好
动态扩容
会发现容量存不下的时候,会重新申请更大的内存空间以及容量
package main
import "fmt"
func main()
var a []int //切片此时还未申请内存
for i := 0 ;i < 10; i++
a = append(a, i)
fmt.Printf("%v len:%d cap:%d ptr:%p\\n", a, len(a), cap(a) ,a)
Operation results:
[0] len:1 cap:1 ptr:0xc000012088
[0 1] len:2 cap:2 ptr:0xc0000120d0
[0 1 2] len:3 cap:4 ptr:0xc0000101e0
[0 1 2 3] len:4 cap:4 ptr:0xc0000101e0
[0 1 2 3 4] len:5 cap:8 ptr:0xc00000e240
[0 1 2 3 4 5] len:6 cap:8 ptr:0xc00000e240
[0 1 2 3 4 5 6] len:7 cap:8 ptr:0xc00000e240
[0 1 2 3 4 5 6 7] len:8 cap:8 ptr:0xc00000e240
[0 1 2 3 4 5 6 7 8] len:9 cap:16 ptr:0xc00001a100
[0 1 2 3 4 5 6 7 8 9] len:10 cap:16 ptr:0xc00001a100
切片扩切片
package main
import "fmt"
func main()
//切片的扩容
var a []int // 还未申请内存
a = append(a, 1,2,3,4,5)
fmt.Println(a)
b := []int12, 13, 14, 15
a = append(a, b...)//三个点代表会把切片b的元素一个个拿出来追加到a后面
fmt.Println(a)
Operation results:
[1 2 3 4 5]
[1 2 3 4 5 12 13 14 15]
切片copy
package main
import "fmt"
func main()
//copy 是传值, := 是传引用(相当于传地址,两个变量操作同一个地址中的东西)
a := []int1,2,3,4,5
b := make([]int, 5, 5)
c := b
copy(b, a)
b[0] = 100
fmt.Println(a)
fmt.Println(b)
fmt.Println(c)
Operation results:
[1 2 3 4 5]
[100 2 3 4 5]
[100 2 3 4 5]
删除切片
package main
import "fmt"
func main()
//切片删除元素
//使用copy方法 append(a[:index], a[index+1:]...)
a := []int11, 22, 33, 44, 55
a = append(a[:2], a[3:]...)//三个点代表会把切片b的元素一个个拿出来追加到a后面
fmt.Println(a)
Operation results:
[11 22 44 55]
map
Go语言中提供的映射关系容器为map,其内部使用散列表(hash)实现。
map是一种无序的基于key-value的数据结构,Go语言中的map是引用类型,必须初始化才能使用。
语法定义:
map[KeyType]ValueType
KeyType:键的类型
ValueType:键对应的值的类型
map类型的变量默认初始值为nil,需要使用make()函数来分配内存。语法为:
make(map[KeyType]ValueType, [cap])
其中cap表示map的容量,该参数虽然不是必须的,但是我们应该在初始化map的时候就为其指定一个合适的容器。
不初始化分配内存就不能直接操作
简单使用
package main
import "fmt"
// map(映射)
func main()
//光声明map类型 但是没有初始化 a就是初始化值nil
var a map[string]int
fmt.Println(a == nil)
//map的初始化
a = make(map[string]int, 8)
fmt.Println(a == nil)
//map中添加键值对
a["山东"] = 100
a["北京"] = 200
fmt.Println(a)
fmt.Printf("a:%#v\\n", a)
fmt.Printf("type:%T\\n", a)
//声明map的同时完成初始化
//如果按照第一种初始化,末尾不加, 会导致报错
b := map[int]bool
1:true,
2:false,
fmt.Println(b)
c := map[string]bool
"y1":true,
"y2":false
fmt.Printf("b:%#v\\n", c)
fmt.Printf("type:%T\\n", c)
Operation results:
true
false
map[北京:200 山东:100]
a:map[string]int“北京”:200, “山东”:100
type:map[string]int
map[1:true 2:false]
b:map[string]bool“y1”:true, “y2”:false
type:map[string]bool
判断键是否在map中
package main
import "fmt"
// map(映射)
func main()
//判断某个键存不存在
var scoreMap = make(map[string]int, 8)
scoreMap["Zhangsan"] = 29
scoreMap["Lisi"] = 76
//判断 wangwu 在不在scoreMap中
value, ok := scoreMap["Wangwu"]
fmt.Println(value, ok)
if ok
fmt.Println("Find Wangwu ok!")
else
fmt.Println("No find!")
Operation results:
0 false
No find!
遍历map
package main
import "fmt"
// map(映射)
func main()
//遍历map
var scoreMap = make(map[string]int, 8)
scoreMap["Zhangsan"] = 29
scoreMap["Lisi"] = 76
//for range
//map 是无序的,键值对和添加的顺序无关
for Key, Value := range scoreMap
fmt.Println(Key, Value)
//只遍历Key
for Key := range scoreMap
fmt.Println(Key)
//只遍历Value
for _, Value := range scoreMap
fmt.Println(Value)
Operation results:
Zhangsan 29
Lisi 76
Zhangsan
Lisi
76
29
删除键值对
使用delete()内建函数从map中删除一组键值对,delete()函数的格式如下:
delete(map, key)
map:表示要删除键值对的map
key:表示要删除的键值对的键
package main
import "fmt"
// map(映射)
func main()
//删除键值对
var scoreMap = make(map[string]int, 8)
scoreMap["Zhangsan"] = 29
scoreMap["Lisi"] = 76
delete(scoreMap, "Zhangsan")
fmt.Println(scoreMap)
Operation results:
map[Lisi:76]
按照某个固定顺序遍历map
会发现map是无序的
Go语言fmt.Sprintf(格式化输出)
package main
import (
"fmt"
"math/rand"
"sort"
"time"
)
// map(映射)
func main()
rand.Seed(time.Now().UnixNano()) //初始化随机数种子
var scoreMap = make(map[string]int, 100)
//添加20个键值对
for i := 0; i < 20; i++
key := fmt.Sprintf("stu%02d", i) //生成stu开头的字符串,格式化字符串并打印输出
value := rand.Intn(100) //0~99的随机整数
scoreMap[key] = value
//按照某个固定顺序遍历map
// for k, v := range scoreMap
// fmt.Println(k, v)
//
//按照key从小到大的顺序去遍历scoreMap
//1.先取出所有的key存放到切片中
keys := make([]string, 0, 100)
for k := range scoreMap
keys = append(keys, k)
//2.对key做排序
sort.Strings(keys) //keys目前是一个有序的切片
//3.按照排序后的key对scoreMap排序
for _, key := range keys
fmt.Println(key, scoreMap[key])
Operation results:
stu00 82
stu01 8
stu02 85
stu03 4
stu04 16
stu05 17
stu06 68
stu07 91
stu08 48
stu09 42
stu10 35
stu11 88
stu12 4
stu13 6
stu14 1
stu15 75
stu16 47
stu17 40
stu18 61
stu19 52
元素为map类型的切片
package main
import (
"fmt"
)
// map(映射)
func main()
var mapSlide = make([]map[string]int, 8, 8) //这里只对了切片初始化,还不能直接操作,还需要对map初始化
fmt.Println(mapSlide == nil) //查看切片是否初始化
fmt.Println(mapSlide[0] == nil) //查看map是否初始化
fmt.Println(mapSlide)
//切片中的map初始化
mapSlide[0] = make(map[string]int, 8)
mapSlide[1] = make(map[string]int, 8)
//初始化后即可对map进行操作
mapSlide[0]["鸡汤MAN"] = 100
mapSlide[1]["尼冲Q币嘛?"] = 12
fmt.Println(mapSlide)
Operation results:
false
true
[map[] map[] map[] map[] map[] map[] map[] map[]]
[map[鸡汤MAN:100] map[尼冲Q币嘛?:12] map[] map[] map[] map[] map[] map[]]
元素为map类型的切片循环初始化
我发现上面初始化其实是有问题的,要一个个初始化,还有后面的长度容量似乎可以不用
package main
import (
"fmt"
)
// map(映射)
func main()
var mapSlide = make([]map[string]int, 8, 8) //这里只对了切片初始化,还不能直接操作,还需要对map初始化
fmt.Println(mapSlide == nil) //查看切片是否初始化
fmt.Println(mapSlide[0] == nil) //查看map是否初始化
fmt.Println(mapSlide)
//切片中的map初始化
for index, _ := range mapSlide
mapSlide[index] = make(map[string]int)
//初始化后即可对map进行操作
mapSlide[0]["鸡汤MAN"] = 100
mapSlide[1]["尼冲Q币嘛?"] = 12
mapSlide[5]["西巴"] = 23
fmt.Println(mapSlide)
Operation results:
false
true
[map[] map[] map[] map[] map[] map[] map[] map[]]
[map[鸡汤MAN:100] map[尼冲Q币嘛?:12] map[] map[] map[] map[西巴:23] map[] map[]]
值为切片的map
package main
import "fmt"
// map(映射)
func main()
//map值为切片
var slideMap = make(map[string][]int, 8)
_, ok := slideMap["中国"]
if ok
fmt.Println(slideMap["中国"])
else
slideMap["中国"] = make([]int, 8, 8)
slideMap["中国"][0] = 10
slideMap["中国"][2] = 49
fmt.Println(slideMap)
Operation results:
map[中国:[10 0 49 0 0 0 0 0]]
map计算字符串每个单词出现的次数
package main
import (
"fmt"
"strings"
)
// map(映射)
func main()
//定义字符出
var TestStr = "How do you do"
//定义map
var WordCount = make(map[string]int, 5)
//字符是用空格来分割的
Words := strings.Split(TestStr, " ") //返回值是个切片
fmt.Printf("%T\\n", Words)
fmt.Println(Words)
for index, word := range Words
fmt.Println("切片下标:", index) //打印切片下标
// fmt.Println(word) //打印切片数据(也就是每个单词)
//map的key就是单词,值就是出现次数,下面的v就是值也就是出现次数,ok代表是否出现过
v, ok := WordCount[word]
if ok //如果出现过,次数加一
WordCount[word] = v + 1
else //第一次出现
WordCount[word] = 1
for k, v := range WordCount
fmt.Println(k, v)
Operation results:
[]string
[How do you do]
切片下标: 0
切片下标: 1
切片下标: 2
切片下标: 3
How 1
do 2
you 1
后面我发现,这个ok判断好像有点多余
package main
import (
"fmt"
"strings"
)
func main()
var TestStr = "How do you do you do"
var WordCount = make(map[string]int, 5)
Words := strings.Split(TestStr, " ")
for _, word := range Words
v, _ := WordCount[word]
WordCount[word] = v + 1
for k, v := range WordCount
fmt.Println(k, v)
Operation results:
How 1
do 3
you 2
以上是关于go map and slice 2021-10-08的主要内容,如果未能解决你的问题,请参考以下文章