一、map
- map和C++中map一样,里面存放的是key-value键值对
- 在Go中map是引用类型,声明语法:var map变量名 map[key的类型]value的类型
结果:package main import "fmt" func main() { var mp map[string]int mpls := map[string]int{"one":1, "two":2, "three":3} mpmk := make(map[string]int) mp = mpls mpmk["语文"] = 1 mpmk["数学"] = 2 mpmk["英语"] = 4 mp["four"] = 4 mp["five"] = 5 fmt.Println("mp[one]", mp["one"]) fmt.Println("mp[ten]", mp["ten"]) fmt.Println("mp", mp) fmt.Println("mpmk[语文]", mpmk["语文"]) fmt.Println(mpmk) }
1.map的容量
- 和数组不一样,map可以根据新增的key-value进行动态的增加删除,所以不存在长度、最大限制;但是可以选择标明map的初始容量capacity,格式如下:
make(map[key类型]value类型, cap)
2.map的遍历
- 遍历map可以用for循环语句和range关键字进行遍历
结果:package main import "fmt" func main() { mpls := map[string]int{"one":1, "two":2, "three":3, "four":4, "five":5, "six":6, "seven":7, "eight":8, "nine":9, "ten":10} //遍历map for key, value := range mpls{ fmt.Printf("key:%s",key) fmt.Printf(": value:%d\n", value) fmt.Println("value:", mpls[key]) } fmt.Println("只遍历key:") //只遍历key for key, _ := range mpls{ fmt.Printf("key:%s ", key) } fmt.Println("\n只遍历value:") //只遍历value for _, value := range mpls{ fmt.Printf("value:%d ", value) } }
3.map中元素的删除
- delete(map变量名, key对应的键)
结果:package main import "fmt" func main() { mpls := map[string]int{"one":1, "two":2, "three":3, "four":4, "five":5, "six":6, "seven":7, "eight":8, "nine":9, "ten":10} //遍历map for key, value := range mpls{ fmt.Printf(" key: %s, value: %d||",key, value) } delete(mpls, "five") delete(mpls,"nine") delete(mpls,"three") fmt.Println("\n删除后:") fmt.Println(mpls) }
4.并发map(sync.Map)
- Go语言中,map在并发情况下,只读是线程安全的,但是同时读写线程是不安全的
- 需要并发读写的时候,一般是用加锁的方式。go中有一个效率高的并发安全的map,即snyc.Map。
- sync.Map有以下特征:
(1)无须初始化,直接声明即可
(2)sync.Map和map不一样,不可以用map的方式对其进行操作。而是有自己特有的方式,Store表示存储,Load表示获取,Delete表示删除
(3)使用Range配合一个回调函数进行遍历错做,通过回调函数返回内部遍历出来的值,Range参数中回调函数的返回值在需要继续迭代遍历时,返回true,终止遍历时返回false - 举例:
结果:package main import ( "fmt" "sync" ) func main() { var snmp sync.Map //存储数据 snmp.Store("语文", 100) snmp.Store("数学", 130) snmp.Store("英语", 120) snmp.Store("生物", 90) snmp.Store("物理", 30) //查看数据 fmt.Println(snmp.Load("生物")) fmt.Println(snmp.Load("数学")) //删除数据 snmp.Delete("生物") //遍历 snmp.Range(func(k, v interface{})bool{ fmt.Println("iterate:", k, v) return true }) }
二、list
- 在Go中,列表list内部的实现原理是双链表,能够高效地进行任意位置元素的插入和删除操作
1.列表的初始化
- 方式一:通过container/list包中的New()函数来进行初始化。变量名 := list.New()
- 方式二:通过var关键字声明初始化list。var 变量名 list.List
2.列表的插入、删除
- 向列表中插入元素,因为是双链表,可以从队尾、队首两头直接插。PushFront向队首前方插入元素,PushBack向队尾后方插入元素,InsertAfter在队列中某元素之后插入一个元素,InsertBefore在队列某元素之前插入一个元素
- Remove移除某个元素
package main
import (
"container/list"
"fmt"
)
func main() {
ls := list.New()
ls.PushFront("d")//队首添加d
ele2 := ls.PushBack("e")//队尾添加e。且存放e元素的句柄。队列:d->e
ele1 := ls.PushFront("c")//队首添加c。且存放c元素的句柄。队列:c->d->e
ls.PushBack("g")//队尾添加f。队列:c->d->e->g
ls.PushFront("a")//队首添加a。队列:a->c->d->e->g
//遍历队列:
for i := ls.Front(); i != nil; i = i.Next(){
fmt.Printf(" %v -> ", i.Value)
}
fmt.Println()
ls.InsertBefore("b", ele1)//在c元素前面添加b。队列:a->b->c->d->e->g
ls.InsertAfter("f", ele2)//在e元素后面添加f。队列:a->b->c->d->e->f->g
ele3 := ls.InsertAfter("i", ele2)//在e元素后面添加i。队列:a->b->c->d->e->i->f->g
//遍历队列:
for i := ls.Front(); i != nil; i = i.Next(){
fmt.Printf(" %v -> ", i.Value)
}
ls.Remove(ele3)//删除元素i
fmt.Println()
//遍历队列:
for i := ls.Front(); i != nil; i = i.Next(){
fmt.Printf(" %v -> ", i.Value)
}
}
结果:
三、零值、空值nil
- 在Go语言中,布尔类型的零值(初始化值)为false,数值类型的零值为0,字符串类型的零值为空字符串"",而指针、切片、映射、通道和接口类型的零值都是nil空值
- Go中,nil是一个预定义好的标识符。
1.nil的特性
(1)nil是不能比较的,无论nil的类型是否相同也都不能进行比较,会编译报错
package main
import "fmt"
func main() {
fmt.Println(nil == nil)
}
结果:(2)nil不是关键字或者保留字
(3)nil没有默认的类型
(4)不同类型的nil的指针是一样的
package main
import (
"fmt"
)
func main() {
var arr[]int
var mp map[int]int
var num int
fmt.Printf("%p\n", arr)
fmt.Printf("%p\n", mp)
fmt.Printf("%p\n", num)
}
结果:
(5)nil是指针、切片、map、channel、接口、函数的零值
package main
import (
"fmt"
)
func main() {
var ptr *string
var slce []int
var arr[]int
var mp map[int]int
var cha chan int
var fn func()
var ins interface{}
fmt.Printf("%#v\n", ptr)
fmt.Printf("%#v\n", slce)
fmt.Printf("%#v\n", arr)
fmt.Printf("%#v\n", mp)
fmt.Printf("%#v\n", cha)
fmt.Printf("%#v\n", fn)
fmt.Printf("%#v\n", ins)
}
结果:
(6)不同类型的nil值占用的内存大小不一样,按照类型大小
package main
import (
"fmt"
"unsafe"
)
func main() {
var ptr *string
var slce []int
var arr[]int
var mp map[int]int
var cha chan int
var fn func()
var ins interface{}
fmt.Println(unsafe.Sizeof(ptr))
fmt.Println(unsafe.Sizeof(slce))
fmt.Println(unsafe.Sizeof(arr))
fmt.Println(unsafe.Sizeof(mp))
fmt.Println(unsafe.Sizeof(cha))
fmt.Println(unsafe.Sizeof(fn))
fmt.Println(unsafe.Sizeof(ins))
}
结果:
四、make和new关键字的区别
- 两个内置函数可以用来在堆上分配内存
- make只能用来分配和初始化slice、map、chan的类型的数据,而new可以分配任意类型的数据
- make分配数据内存返回的是引用,即Type,而new返回一个指向接收参数类型的指针,即*Type。(make 关键字的主要作用是创建 slice、map 和 Channel 等内置的数据结构,而 new 的主要作用是为类型申请一片内存空间,并返回指向这片内存的指针。
) - 接收参数个数不一样:make() 只接收一个参数,而 new() 可以接收多个参数
- make分配的空间后,会进行初始化。而new分配的空间会清零