温柔与霸道 發表於 2020-4-25 23:24:00

Go中的map和指针

<p>本文参考:https://www.liwenzhou.com/posts/Go/08_map/</p>
<h2 id="map映射">MAP(映射)</h2>
<p>Go语言中提供的映射关系容器为<code>map</code>,其内部使用<code>散列表(hash)</code>实现。(类似于Python中的字典dict)</p>
<h3 id="映射概述">映射概述</h3>
<p>map是一种无序的基于<code>key-value</code>的数据结构,Go语言中map是引用类型,必须初始化后才能使用。</p>
<h3 id="创建map">创建map</h3>
<p>Go语言中<code>map</code>的定义语法如下:</p>
<pre><code class="language-go">mapvalueType

//keyType 表示键的类型
//valueType表示键对应值的类型
</code></pre>
<p>map类型的变量默认初始值为nil,需要使用make()函数来分配内存。</p>
<pre><code class="language-go">make(mapValueType,cap)

// cap表示map的容量,不指定时默认为0,但是我们应该在初始化map的时候就为其指定一个合适的容量
</code></pre>
<p>示例:</p>
<pre><code class="language-go">func main(){
        // 在声明的时候填充元素
        userInfo := mapstring{
                "username":"Negan"
                "password":"mima"
        }
       
        // 使用内建函数make()创建
        scoreMap:=make(mapint,8)
        scoreMap["张三"] = 90
        scoreMap["李四"] = 100
        fmt.Println(scoreMap)   //map[张三:90 李四:100]
        fmt.Println(scoreMap["李四"]) //100
        fmt.Printf("type of a:%T\n",scoreMap) //type of a:mapint
   
    //创建一个string为键,值为任意类型的map
    userMap := make(mapinterface{})
    userMap["name"] = "Negan"
    userMap["age"] = 68
}
</code></pre>
<h3 id="判断某个键是否存在">判断某个键是否存在</h3>
<p>Go语言中有个判断map中键是否存在的特殊写法</p>
<pre><code class="language-go">value,ok:=map
</code></pre>
<p>示例:</p>
<pre><code class="language-go">func main(){
        scoreMap := make(mapint)
        scoreMap["张三"] = 90
        scoreMap["李四"] = 100
        // 如果key存在ok为true,v为对应的值,不存在ok为false,v为值类型的零值
        v,ok := scoreMap["张三"]
        if ok{
                fmt.Println(v)
        }else{
                fmt.Println("查无此人")
        }
}
</code></pre>
<h3 id="map的遍历">map的遍历</h3>
<p>range关键字对map遍历,可以同时得到键值对的key和value,也可仅仅得到key</p>
<pre><code class="language-go">func main(){
        scoreMap := make(mapint)
        scoreMap["张三"] = 90
        scoreMap["李四"] = 100
        // 遍历key和value
        for k,v := range scoreMap{
                fmt.Println(k,v)
        }
        //仅仅遍历key
        for k := range scoreMap{
                fmt.Println(k)
        }
}
</code></pre>
<ul>
<li>注意:遍历map时的元素顺序与添加键值对的顺序无关。</li>
</ul>
<h3 id="按照指定循序遍历map">按照指定循序遍历map</h3>
<pre><code class="language-go">func main(){
    rand.Seed(time.Now().UnixNano())// 初始化随机数种子
   
    scoreMap := make(mapint,200)
   
    for i:=0;i&lt;100;i++{
      key := fmt.Sprintf("stu%02d",i)// 生成stu开头的字符串
      value := rand.Intn(100)// 生成0-99的随机整数
      scoreMap = 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)
    }
}
</code></pre>
<h3 id="使用delete函数删除键值对">使用delete()函数删除键值对</h3>
<p>使用<code>delete()</code>内建函数从map中删除一组键值对,<code>delete()</code>函数的格式如下:</p>
<pre><code class="language-go">delete(map,key)

// map:表示要删除键值对的map
// key:表示要删除的键值对的键
</code></pre>
<p>示例:</p>
<pre><code class="language-go">func main(){
        scoreMap := make(mapint)
        scoreMap["张三"] = 90
        scoreMap["李四"] = 100
       
        delete(scoreMap,"张三")// 将张三:90从map中删除
        fmt.Println(scoreMap)
}
</code></pre>
<h3 id="元素为map类型的切片">元素为map类型的切片</h3>
<p>下面代码延时了切片中的元素为map类型时的操作:</p>
<pre><code class="language-go">func main(){
        var mapSlice = make([]mapstring,3)
        for index,value := range mapSlice{
                fmt.Printf("index:%d value:%v\n",index,value)
        }
        fmt.Println("after init")
        // 对切片中的map元素进行初始化
        mapSlice = make(mapinterface{},10)
        mapSlice["name"] = "Negan"
        mapSlice["age"] = 68
        mapSlice["hobby"] = "棒球"
   
    for index,value:= range mapSlice{
      fmt.Printf("index:%d value:%v\n",index, value)
    }
}
</code></pre>
<h3 id="值为切片类型的map">值为切片类型的map</h3>
<p>下面代码演示map中值为切片类型的操作</p>
<pre><code class="language-go">func main{
        sliceMap := make(map[]string,3)
        fmt.Println(sliceMap)
        fmt.Println("after init")
        key := "中国"
        value,ok := sliceMap
        if !ok{
                value = make([]string,0,2)
        }
        value = append(value, "北京","上海","西安")
        sliceMap[key = value
        fmt.Println(sliceMap)
}
</code></pre>
<h3 id="练习题">练习题</h3>
<p>1、写一个程序,统计一个字符串中每个单词出现的次数。比如:“how do you do”中how=1 do=2 you=1。</p>
<pre><code class="language-go">func main(){
    s := “how do you do”
    s_sclice := strings.Split(s," ")// 分割字符串
   
    count_map := make(mapint)
    for key := range s_sclice{
      _,ok:=count_map
      if !ok{
            count_map = 1
      }else{
            count_map += 1
      }
    }
    fmtPrintln(count_map)
}
</code></pre>
<h2 id="指针">指针</h2>
<p>Go中的指针区别于C/C++中的指针,Go语言中的指针不能进行便宜和运算,是安全指针。</p>
<p>任何数据载入内存后,在内存都有它们的地址,这就是指针。为了保存一个数据在内存中的地址,我们就需要指针变量。指针变量的值就是内存中的一块地址编号。</p>
<p>比如:“卑鄙是卑鄙者的通行证,高尚是高尚的墓志铭”,想把它写入程序中,程序一启动,这句话是要加载到内存(假设内存地址ox123456),在程序中把这句诗赋值给变量<code>A</code>,把内存地址赋值给变量<code>B</code>。这时候变量<code>B</code>就是一个指针变量。通过变量<code>A</code>和<code>B</code>都能找到这句诗。</p>
<p>Go语言中的指针不能进行偏移和运算,因此Go语言中的指针操作非常简单,只需要记住两个符号,<code>&amp;</code>(取地址)和<code>*</code>(根据地址取值)</p>
<h3 id="指针地址和指针类型">指针地址和指针类型</h3>
<p>每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置。</p>
<p>Go语言中使用<code>&amp;</code>字符放在变量前面对变量进行“取地址操作”。Go语言中值类型(int、float、bool、string、array、struct)都有对应的指针类型。</p>
<p>如<code>*int</code>、<code>*int64</code>、<code>*string</code>等。</p>
<p>取变量指针语法如下:</p>
<pre><code class="language-go">ptr := &amp;v // v的类型为T

// v:代表被取地址的变量,类型为T
// ptr:用于接收地址的变量,ptr的类型就是*T,称为T的指针类型。
</code></pre>
<p>示例:</p>
<pre><code>func main(){
        a := 10
        b := &amp;a
        fmt.Printf("a:%d ptr:%p\n", a, &amp;a)//a:10 ptr:0xc00001a078
        fmt.Printf("b:%p type:%T\n", b, b)//b:0xc00001a078 type:*int
        fmt.Println(&amp;b)   // 0xc00000e018
}
</code></pre>
<p><img src="https://img2020.cnblogs.com/blog/1341397/202004/1341397-20200423162956754-1880006588.png" alt="" loading="lazy"></p>
<h3 id="指针取值">指针取值</h3>
<p>在对普通变量使用&amp;操作符取地址后获得这个变量的指针,然后可以对指针使用*操作。也就是指针取值。</p>
<pre><code class="language-go">func main(){
        // 指针取值
        a := 10
        b := &amp;a   // 取变量a的地址,将指针保存到b
        fmt.Printf("type of b:%T\n",b)   // type of b:*int
        c := *b// 指针取值(根据指针去内存取值)
        fmt.Printf("type of c:%T\n", c)   // type of c:int
        fmt.Printf("value of c:%v\n", c)// value of c:10
}
</code></pre>
<p>总结:取地址操作符<code>&amp;</code>和取值操作符<code>*</code>是一对互补操作符。</p>
<p>​                        <code>&amp;</code>取出地址,<code>*</code>根据地址取出地址指向的值</p>
<p>变量、指针地址、指针变量、取地址、取值的相互关系和特性如下:</p>
<ul>
<li>对变量进行取地址(&amp;)操作,可以获取这个变量的指针变量</li>
<li>指针变量的值是指针地址</li>
<li>对指针变量进行取值(*)操作,可以获得指针变量指向的原变量的值。</li>
</ul>
<h4 id="指针传值示例">指针传值示例</h4>
<pre><code class="language-go">func modify1(x int){
        x = 100
}

func modify2(x *int){
        *x = 100
}

func main(){
        a := 10
        modify1(a)
        fmt.Println(a)   // 10
        modify2(&amp;a)
        fmt.Println(a)   // 100
}
</code></pre>
<h3 id="new和make">new和make</h3>
<p>在Go语言中对于引用类型的变量,在使用时不仅要声明它,还要为它分配内存空间,否则我们的值没办法进行存储,而对于值类型的声明,不需要分配内存空间,是因为它们在声明的时候已经默认分配好了内存空间。</p>
<pre><code class="language-go">func main(){
        var a *int
        *a = 100
        fmt.Println(*a)   // 引发panic
       
        var b map["string"]int
        b["age"] = 10
        fmt.Println(b)   // 引发panic
}
</code></pre>
<h4 id="new">new</h4>
<p>new是一个内置函数,函数签名如下:</p>
<pre><code class="language-go">func new(Type) *Type

// Type 表示类型,new函数只接受一个参数,这个参数是一个类型
// *Type 表示类型指针,new函数返回一个指向该类型内存地址的指针
</code></pre>
<p>new函数不太常用,使用new函数得到的是一个类型的指针,并且该指针对应的值为该类型的零值。</p>
<pre><code class="language-go">func main(){
        a := new(int)
        b := new(bool)
        fmt.Prtintf("%T\n",a)// *int
        fmt.Printf("%T\n", b)// *bool
        fmt.Printf(*a)// 0
        fmt.Printf(*b)//false
}
</code></pre>
<p>刚开始我们的实例代码中<code>var a *int</code>只是声明一个指针变量a,但是并没有初始化,指针座位引用类型,需要初始化后才能拥有内存空间,才可以给它进行复制。</p>
<pre><code class="language-go">func main(){
        var a *int   // 声明a为int类型的指针
        a = new(int)// 分配内存空间
        *a = 10
        fmt.Println(*a)
}
</code></pre>
<h4 id="make">make</h4>
<p>make也是用来分配内存的,区别于new,它只用于slice,map以及chan的内存创建。而且返回的类型就是这三个类型的本身。而不是它们的指针类型。因为这三种类型本身就是引用类型。所以没必要返回它们的指针类型。</p>
<pre><code class="language-go">func make(t Type, size ...IntegerType) Type
</code></pre>
<p>make函数是无可替代的,我们在使用slice、map以及channel的时候,都需要使用make进行初始化,然后才可以对它们进行操作。</p>
<p>刚开始的示例中<code>var b mapint</code>只是生命变量b是一个map类型的变量。不能直接进行给它复制,需要使用make函数进行操作之后,才能对其进行键值对赋值。</p>
<pre><code class="language-go">func main(){
        var b mapint
        b = make(mapint,10)
        b["age"] = 10
        fmt.Println(b)
}
</code></pre>
<h3 id="new和make的区别">new和make的区别</h3>
<p>两者都是用来做内存分配的。</p>
<p>make只用于slice、map、以及channel的初始化,返回的还是这三个引用类型本身</p>
<p>new用于类型的内存分配,并且内存对应的值为类型零值,返回的是指向类型的指针。</p>
<p>本文参考:https://www.liwenzhou.com/posts/Go/08_map/</p><br><br>
来源:https://www.cnblogs.com/huiyichanmian/p/12761754.html
頁: [1]
查看完整版本: Go中的map和指针