zoukankan      html  css  js  c++  java
  • Go中的map和指针

    本文参考:https://www.liwenzhou.com/posts/Go/08_map/

    MAP(映射)

    Go语言中提供的映射关系容器为map,其内部使用散列表(hash)实现。(类似于Python中的字典dict)

    映射概述

    map是一种无序的基于key-value的数据结构,Go语言中map是引用类型,必须初始化后才能使用。

    创建map

    Go语言中map的定义语法如下:

    map[keyType]valueType
    
    //keyType 表示键的类型
    //valueType表示键对应值的类型
    

    map类型的变量默认初始值为nil,需要使用make()函数来分配内存。

    make(map[keyType]ValueType,cap)
    
    // cap表示map的容量,不指定时默认为0,但是我们应该在初始化map的时候就为其指定一个合适的容量
    

    示例:

    func main(){
    	// 在声明的时候填充元素
    	userInfo := map[string]string{
    		"username":"Negan"
    		"password":"mima"
    	}
    	
    	// 使用内建函数make()创建
    	scoreMap:=make(map[string]int,8)
    	scoreMap["张三"] = 90
    	scoreMap["李四"] = 100
    	fmt.Println(scoreMap)   //map[张三:90 李四:100]
    	fmt.Println(scoreMap["李四"]) //100
    	fmt.Printf("type of a:%T
    ",scoreMap) //type of a:map[string]int
        
        //创建一个string为键,值为任意类型的map
        userMap := make(map[string]interface{}) 
        userMap["name"] = "Negan"
        userMap["age"] = 68
    }
    

    判断某个键是否存在

    Go语言中有个判断map中键是否存在的特殊写法

    value,ok:=map[key]
    

    示例:

    func main(){
    	scoreMap := make(map[string]int)
    	scoreMap["张三"] = 90
    	scoreMap["李四"] = 100
    	// 如果key存在ok为true,v为对应的值,不存在ok为false,v为值类型的零值
    	v,ok := scoreMap["张三"]
    	if ok{
    		fmt.Println(v)
    	}else{
    		fmt.Println("查无此人")
    	}
    }
    

    map的遍历

    range关键字对map遍历,可以同时得到键值对的key和value,也可仅仅得到key

    func main(){
    	scoreMap := make(map[string]int)
    	scoreMap["张三"] = 90
    	scoreMap["李四"] = 100
    	// 遍历key和value
    	for k,v := range scoreMap{
    		fmt.Println(k,v)
    	}
    	//仅仅遍历key
    	for k := range scoreMap{
    		fmt.Println(k)
    	}
    }
    
    • 注意:遍历map时的元素顺序与添加键值对的顺序无关。

    按照指定循序遍历map

    func main(){
        rand.Seed(time.Now().UnixNano())  // 初始化随机数种子
        
        scoreMap := make(map[string]int,200)
        
        for i:=0;i<100;i++{
            key := fmt.Sprintf("stu%02d",i)  // 生成stu开头的字符串
            value := rand.Intn(100)  // 生成0-99的随机整数
            scoreMap[key] = value
        }
        // 取出map中所有的key存放到切片keys中
        keys := make([]string,0,200)
        for key:=range scoreMap{
            keys = append(keys, key)
        }
        // 对切片进行排序
        sort.Strings(keys)
        // 按照排序后的key遍历map
        for _,key := range keys{
            fmt.Println(key, scoreMap[key])
        }
    }
    

    使用delete()函数删除键值对

    使用delete()内建函数从map中删除一组键值对,delete()函数的格式如下:

    delete(map,key)
    
    // map:表示要删除键值对的map
    // key:表示要删除的键值对的键
    

    示例:

    func main(){
    	scoreMap := make(map[string]int)
    	scoreMap["张三"] = 90
    	scoreMap["李四"] = 100
    	
    	delete(scoreMap,"张三")  // 将张三:90从map中删除
    	fmt.Println(scoreMap)
    }
    

    元素为map类型的切片

    下面代码延时了切片中的元素为map类型时的操作:

    func main(){
    	var mapSlice = make([]map[string]string,3)
    	for index,value := range mapSlice{
    		fmt.Printf("index:%d value:%v
    ",index,value)
    	}
    	fmt.Println("after init")
    	// 对切片中的map元素进行初始化
    	mapSlice[0] = make(map[string]interface{},10)
    	mapSlice[0]["name"] = "Negan"
    	mapSlice[0]["age"] = 68
    	mapSlice[0]["hobby"] = "棒球"
        
        for index,value:= range mapSlice{
            fmt.Printf("index:%d value:%v
    ",index, value)
        }
    }
    

    值为切片类型的map

    下面代码演示map中值为切片类型的操作

    func main{
    	sliceMap := make(map[string][]string,3)
    	fmt.Println(sliceMap)
    	fmt.Println("after init")
    	key := "中国"
    	value,ok := sliceMap[key]
    	if !ok{
    		value = make([]string,0,2)
    	}
    	value = append(value, "北京","上海","西安")
    	sliceMap[key = value
    	fmt.Println(sliceMap)
    }
    

    练习题

    1、写一个程序,统计一个字符串中每个单词出现的次数。比如:“how do you do”中how=1 do=2 you=1。

    func main(){
        s := “how do you do”
        s_sclice := strings.Split(s," ")  // 分割字符串
        
        count_map := make(map[string]int)
        for key := range s_sclice{
            _,ok:=count_map[key]
            if !ok{
                count_map[key] = 1
            }else{
                count_map[key] += 1
            }
        }
        fmtPrintln(count_map)
    }
    

    指针

    Go中的指针区别于C/C++中的指针,Go语言中的指针不能进行便宜和运算,是安全指针。

    任何数据载入内存后,在内存都有它们的地址,这就是指针。为了保存一个数据在内存中的地址,我们就需要指针变量。指针变量的值就是内存中的一块地址编号。

    比如:“卑鄙是卑鄙者的通行证,高尚是高尚的墓志铭”,想把它写入程序中,程序一启动,这句话是要加载到内存(假设内存地址ox123456),在程序中把这句诗赋值给变量A,把内存地址赋值给变量B。这时候变量B就是一个指针变量。通过变量AB都能找到这句诗。

    Go语言中的指针不能进行偏移和运算,因此Go语言中的指针操作非常简单,只需要记住两个符号,&(取地址)和*(根据地址取值)

    指针地址和指针类型

    每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置。

    Go语言中使用&字符放在变量前面对变量进行“取地址操作”。Go语言中值类型(int、float、bool、string、array、struct)都有对应的指针类型。

    *int*int64*string等。

    取变量指针语法如下:

    ptr := &v // v的类型为T
    
    // v:代表被取地址的变量,类型为T
    // ptr:用于接收地址的变量,ptr的类型就是*T,称为T的指针类型。
    

    示例:

    func main(){
    	a := 10
    	b := &a
    	fmt.Printf("a:%d ptr:%p
    ", a, &a)  //a:10 ptr:0xc00001a078
    	fmt.Printf("b:%p type:%T
    ", b, b)  //b:0xc00001a078 type:*int
    	fmt.Println(&b)   // 0xc00000e018
    }
    

    指针取值

    在对普通变量使用&操作符取地址后获得这个变量的指针,然后可以对指针使用*操作。也就是指针取值。

    func main(){
    	// 指针取值
    	a := 10
    	b := &a   // 取变量a的地址,将指针保存到b
    	fmt.Printf("type of b:%T
    ",b)   // type of b:*int
    	c := *b  // 指针取值(根据指针去内存取值)
    	fmt.Printf("type of c:%T
    ", c)   // type of c:int
    	fmt.Printf("value of c:%v
    ", c)  // value of c:10
    }
    

    总结:取地址操作符&和取值操作符*是一对互补操作符。

    &取出地址,*根据地址取出地址指向的值

    变量、指针地址、指针变量、取地址、取值的相互关系和特性如下:

    • 对变量进行取地址(&)操作,可以获取这个变量的指针变量
    • 指针变量的值是指针地址
    • 对指针变量进行取值(*)操作,可以获得指针变量指向的原变量的值。

    指针传值示例

    func modify1(x int){
    	x = 100
    }
    
    func modify2(x *int){
    	*x = 100
    }
    
    func main(){
    	a := 10
    	modify1(a)
    	fmt.Println(a)   // 10 
    	modify2(&a)
    	fmt.Println(a)   // 100  
    }
    

    new和make

    在Go语言中对于引用类型的变量,在使用时不仅要声明它,还要为它分配内存空间,否则我们的值没办法进行存储,而对于值类型的声明,不需要分配内存空间,是因为它们在声明的时候已经默认分配好了内存空间。

    func main(){
    	var a *int
    	*a = 100
    	fmt.Println(*a)   // 引发panic
    	
    	var b map["string"]int
    	b["age"] = 10
    	fmt.Println(b)   // 引发panic
    }
    

    new

    new是一个内置函数,函数签名如下:

    func new(Type) *Type
    
    // Type 表示类型,new函数只接受一个参数,这个参数是一个类型
    // *Type 表示类型指针,new函数返回一个指向该类型内存地址的指针
    

    new函数不太常用,使用new函数得到的是一个类型的指针,并且该指针对应的值为该类型的零值。

    func main(){
    	a := new(int)
    	b := new(bool)
    	fmt.Prtintf("%T
    ",a)  // *int
    	fmt.Printf("%T
    ", b)  // *bool
    	fmt.Printf(*a)  // 0
    	fmt.Printf(*b)  //false
    }
    

    刚开始我们的实例代码中var a *int只是声明一个指针变量a,但是并没有初始化,指针座位引用类型,需要初始化后才能拥有内存空间,才可以给它进行复制。

    func main(){
    	var a *int   // 声明a为int类型的指针
    	a = new(int)  // 分配内存空间
    	*a = 10
    	fmt.Println(*a)
    }
    

    make

    make也是用来分配内存的,区别于new,它只用于slice,map以及chan的内存创建。而且返回的类型就是这三个类型的本身。而不是它们的指针类型。因为这三种类型本身就是引用类型。所以没必要返回它们的指针类型。

    func make(t Type, size ...IntegerType) Type
    

    make函数是无可替代的,我们在使用slice、map以及channel的时候,都需要使用make进行初始化,然后才可以对它们进行操作。

    刚开始的示例中var b map[string]int只是生命变量b是一个map类型的变量。不能直接进行给它复制,需要使用make函数进行操作之后,才能对其进行键值对赋值。

    func main(){
    	var b map[string]int
    	b = make(map[string]int,10)
    	b["age"] = 10
    	fmt.Println(b)
    }
    

    new和make的区别

    两者都是用来做内存分配的。

    make只用于slice、map、以及channel的初始化,返回的还是这三个引用类型本身

    new用于类型的内存分配,并且内存对应的值为类型零值,返回的是指向类型的指针。

    本文参考:https://www.liwenzhou.com/posts/Go/08_map/

  • 相关阅读:
    常见面试题
    性能测试注意点
    orm 事物的使用
    mvc 页面如何引用命名空间并且直接使用枚举类型对象
    ef 动态拼接参数查询
    ef指定字段更新
    jquery 如何传递对象本身
    整数除以整数后转成百分比并且保留一位小数
    sql 表变量的使用
    echart的label标签文字过长显示不全怎么办?
  • 原文地址:https://www.cnblogs.com/huiyichanmian/p/12761754.html
Copyright © 2011-2022 走看看