前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >你不知道的Golang盲点汇总【持续更新】

你不知道的Golang盲点汇总【持续更新】

作者头像
sunsky
发布2020-08-20 17:35:25
7420
发布2020-08-20 17:35:25
举报
文章被收录于专栏:sunskysunsky

1. 引用传递时append会复制生成新的指针

output:

out of w [1] (*int)(0x40e020) in w [1] (*int)(0x40e020) after append w [1 0] (*int)(0x40e040) after w [1] (*int)(0x40e020) [1 0] (*int)(0x40e040)

2. 空值append

package main

import (
    "fmt"
)

func main() {
    s:=[][]int{}
    //使用...更加方便,还不会导致塞入空值
    fmt.Println("Hello, playground", append(s, [][]int{}... ), append(s, []int{} ))
}

output:

Hello, playground [] [[]]

3. 数组、slice、切片的雷!

package main

import (
    "fmt"
    "reflect"
)

func main() {
    //固定长度是array类型,可以覆盖成员,但不能修改长度,即不能append 
    a := [4]int{1,2}
    a[0]=9
    //没设置长度是slice类型,即动态或不定长数组, 也能覆盖成员,也修改长度,即支持append
    s := []int{1,2}
    //s[1:len(s):cap(s)] 等效 s[1:len(s)]
    s[0]=9
    //注意下村是:左开(含左边元素),右闭(不含右边元素)
    fmt.Printf("%#v %#v %v %v %#v %#v %#v \n", a ,s, reflect.TypeOf(a).Kind(), reflect.TypeOf(s).Kind(), a[1:len(a)], s[1:len(s):cap(s)],s[1:len(s)],  )
    
    //subslice切片
    //切片,也叫subslice,是一块基于父slice的投影
    subArray:=a[0:2]
    //报错:invalid slice index: 3 > 2, 即len不能超过cap
    //subArray2:=subArray[0:3:2]
    subArray2:=subArray[0:1:2]
    //报错:index out of range [1] with length 1
    //subArray2[1]=222
    //报错:index out of range [1] with length 1,len+offset>父slice的cap ,投影不能超出父slice的cap区域
    //subArray3:=subArray2[1:2:2]
    subArray3:=subArray2[0:]
    //切片是指定父slice的指针,所以会修改父slice,使用时一定要小心!!!
    subArray3[0]=222
    
    //subArray虽然是新变量名,但实际指定同一个内存地址
    fmt.Printf("subslice %#v %#v  %#v  %#v  %v %v %v %v\n", subArray, a, subArray2,subArray3, &subArray[0], &a[0], &subArray2[0],&subArray3[0])
    
    
    //扩容测试
    fmt.Printf("%v %v \n", len(s), cap(s))
    s=append(s, 1)
    fmt.Printf("%v %v \n", len(s), cap(s))
    s=append(s, 1)
    fmt.Printf("%v %v \n", len(s), cap(s))
    //tmp:=[8]int{}
    //错误:cannot use tmp (type [8]int) as type []int in append
    //正确玩法:
    //不能用Printf中有unicode,否则会 format %扩 has unknown verb 扩
    fmt.Println("len增加且超过cap时,若<=1024,以100%扩容 ")
    s=[]int{}
    i:=64
    for i>=0 {
        fmt.Printf("%v %v \n", len(s), cap(s)) 
        s=append(s,1)
        i-=1
    }

    fmt.Println("len增加且超过cap>1024时,否则以大约25%扩容,注意是大约")
    //大约25%扩容详细参考: https://juejin.im/post/5ca4239ef265da30807fea48
    i=1028
    s=[]int{}
    for i>=0 {
        s=append(s,1)
        i-=1
        if i%100==0{
            fmt.Printf("%v %v \n", len(s), cap(s)) 
        }
    }
    
}

output:

[4]int{9, 2, 0, 0} []int{9, 2} array slice []int{2, 0, 0} []int{2} []int{2} 
subslice []int{222, 2} [4]int{222, 2, 0, 0}  []int{222}  []int{222}  0x40e020 0x40e020 0x40e020 0x40e020
2 2 
3 4 
4 4 
len增加且超过cap时,若<=1024,以100%扩容 
0 0 
1 2 
2 2 
3 4 
4 4 
5 8 
6 8 
7 8 
8 8 
9 16 
10 16 
11 16 
12 16 
13 16 
14 16 
15 16 
16 16 
17 32 
18 32 
19 32 
20 32 
21 32 
22 32 
23 32 
24 32 
25 32 
26 32 
27 32 
28 32 
29 32 
30 32 
31 32 
32 32 
33 64 
34 64 
35 64 
36 64 
37 64 
38 64 
39 64 
40 64 
41 64 
42 64 
43 64 
44 64 
45 64 
46 64 
47 64 
48 64 
49 64 
50 64 
51 64 
52 64 
53 64 
54 64 
55 64 
56 64 
57 64 
58 64 
59 64 
60 64 
61 64 
62 64 
63 64 
64 64 
len增加且超过cap>1024时,否则以大约25%扩容,注意是大约
28 32 
128 128 
228 256 
328 512 
428 512 
528 1024 
628 1024 
728 1024 
828 1024 
928 1024 
1028 1344 

4. 字符串不能随意for

package main

import (
    "fmt"
)

func main() {
    s:="abc中国人"
    
    fmt.Println("for len是逐个unicode,也叫rune")
    for k,v:=range s {
        fmt.Println(k, string(v))
    }
    fmt.Println("for len是逐个字节, 非ascii就会乱")
    for i:=0;i<len(s);i++{
        fmt.Println(i, string(s[i]))
    }
    fmt.Println("正确用法:")
    //for len是逐个字节
    u:=[]rune(s)
    l:=len(u)
    for i:=0;i<l;i++{
        fmt.Println(i, string(u[i]))
    }
}

output:

for len是逐个unicode,也叫rune
0 a
1 b
2 c
3 中
6 国
9 人
for len是逐个字节, 非ascii就会乱
0 a
1 b
2 c
3 ä
4 ¸
5 ­
6 å
7 ›
8 ½
9 ä
10 º
11 º
正确用法:
0 a
1 b
2 c
3 中
4 国
5 人

4. map是无序的

package main

import (
    "fmt"
)

func main() {
    m:=map[string]string{
    "cc":"CC",
    "aa":"AA",
    "bb":"BB",
    }
    for k,v:=range(m){
        fmt.Println(k,v)
    }
    fmt.Println("")
    for k,v:=range(m){
        fmt.Println(k,v)
    }
    fmt.Println("")
    for k,v:=range(m){
        fmt.Println(k,v)
    }
    fmt.Println("")
    // sort 'string' key in increasing order
    fmt.Println("fix : ")
    for k,v:=range(m){
        fmt.Println(k,v)
    }    

    
}

output:

cc CC
aa AA
bb BB

cc CC
aa AA
bb BB

bb BB
cc CC
aa AA

fix : 
cc CC
aa AA
bb BB

原因:

根据Go规范, map上的迭代顺序是不确定的,并且在程序运行之间可能会有所不同。实际上,不仅它是不确定的,而且实际上是有意随机化的。这是因为它曾经是可预测的,并且Go语言开发人员不希望人们依赖未指定的行为,所以他们有意地将其随机化,因此不可能依赖此行为。维护原插入顺序需要更多内存和cpu操作,Go设计简洁,无序意味着性能更佳,且不是所有地方都需要顺序。想排序或者按插入顺序,很容易自行实现。在Go 1.12+中,您只需打印一个map,它将自动按键排序。之所以添加它,是因为它可以轻松测试map。

原文:https://blog.golang.org/go-maps-in-action 中 When iterating over a map with a range loop, the iteration order is not specified and is not guaranteed to be the same from one iteration to the next. If you require a stable iteration order you must maintain a separate data structure that specifies that order。

参考 : https://tip.golang.org/doc/go1.12#fmt

解决:package mainimport ( "fmt" "sort"

)
//按插入的顺序
type RawSortedMap struct {
  //map key类型不能是slices, maps, and functions; these types cannot be compared using ==, and may not be used as map keys.
    m map[string]interface{}
    keys []interface{}
}

func NewRawSortedMap(cap int)RawSortedMap{
    m := make(map[string]interface{}, cap)
    keys:=make([]interface{}, cap)
    n:=RawSortedMap{m, keys}
    return n
}



func (r *RawSortedMap) Set(k string, v interface{}) {
    //通过k 快速查找
  //map不能同时read 和write,否则请加sync.RWMutex锁或者CAS .
  r.m[k] = len(r.m)
    //通过keys数组快速遍历
    r.keys = append(r.keys, v)
}
func main() {

    m := make(map[int]string)
    m[1] = "a"
    m[2] = "c"
    m[0] = "b"

    // To store the keys in slice in sorted order
    var keys []int
    for k := range m {
        keys = append(keys, k)
    }
    //按数字大小的顺序
    sort.Ints(keys)
    //按字母的顺序
    //sort.Strings(keys)

    // To perform the opertion you want
    for _, k := range keys {
        fmt.Println("Key:", k, "Value:", m[k])
    }
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-11-06 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档