Go语言容器之map、list和nil

本文介绍了Go语言中的map数据结构,包括其动态扩容、遍历及删除元素的方法。同时,详细阐述了并发环境下如何使用sync.Map保证线程安全。此外,还讨论了Go语言中的零值nil,以及make和new关键字的区别,强调了它们在分配内存和初始化方面的差异。
摘要由CSDN通过智能技术生成

一、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分配的空间会清零
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值