代码改变世界

golang数组切片map chan 结构体 初始化

2022-03-24 14:02  youxin  阅读(1448)  评论(0编辑  收藏  举报

Golang当中有三种初始化的方法,

var v2 int = 10
var v3 = 10
v4 := 10
var 变量名 类型 = 表达

编译器推导类型的格式

在标准格式的基础上,将 int 省略后,编译器会尝试根据等号右边的表达式推导 hp 变量的类型。

var hp = 100

等号右边的部分在编译原理里被称做右值(rvalue)。

 

短变量声明并初始化
var 的变量声明还有一种更为精简的写法,例如:

hp := 100

如果 hp 已经被声明过,但依然使用:=时编译器会报错,代码如下:

no new variables on left side of :=

意思是,在“:=”的左边没有新变量出现,意思就是“:=”的左边变量已经被声明了。

 

一、数组初始化方式

  • var [length]Type
var array [5]int //这种方式,只是初始化,不带初始化值,数组长度,已经定义好, 但是其实初始化的值,已经有了并且是该类型的最小值(bool false),int 0, string ' ' 其他,自行验证
  • var [N]Type{value1, value2, ... , valueN}
var array  = [5]int{1, 2, 3, 4, 5}  // 这种方式,既初始化变量,又带了初始化值,数组长度已经定义好

Initializing an array using an array literal
You can declare and initialize an array at the same time like this -

// Declaring and initializing an array at the same time
var a = [5]int{2, 4, 6, 8, 10}

 

  • var [...]Type{value1, value2, ... , valueN}
var array  = [...]int{1, 2, 3, 4, 5} // 这种方式,既初始化变量,也是带了初始值,数组长度,根据初始值的个数而定,也就是五个

// Letting Go compiler infer the length of the array
a := [...]int{3, 5, 7, 9, 11, 13, 17}

  • :=[N]Type{value1, value2, ... , valueN}
array :=[5]int{1, 2, 3, 4, 5} // 这种方式,省去 var 关键词,将初始化变量和赋值,放在一起操作,这种方式简单,明了。
  • := [...]Type{value1, value2, ... , valueN}
array := [...]int{1, 2, 3, 4, 5} // 这种方式,既初始化变量,也是带了初始值,数组长度,根据初始值的个数而定,也就是五个

多维数组
a := [2][2]int{
        {3, 5},
        {7, 9},    // This trailing comma is mandatory
    }
    fmt.Println(a)

    // Just like 1D arrays, you don't need to initialize all the elements in a multi-dimensional array.
    // Un-initialized array elements will be assigned the zero value of the array type.
    b := [3][4]float64{
        {1, 3},
        {4.5, -3, 7.4, 2},
        {6, 2, 11},
    }

 


二、切片初始化方式

  • make ( []Type ,length, capacity )
slice := make([]int, 3, 5) 
  • make ( []Type, length)
slice := make([]int, 5)
  • []Type{}
slice := []int{}

[]string{} 这种方法,初始化成一个大小为0的slice,此时变量(s == nil)已经不成立了,但是s的大小len(s)还是等于0。实际上 []string{} == make([]string, 0)
 
  • []Type{value1 , value2 , ... , valueN }
slice := []int{1, 2, 3, 4, 5} // 这种方式,len是根据初始化长度而定的


为什么什么在初始化slice的时候尽量补全cap

arr := []int{}
arr = append(arr, 1,2,3,4, 5)

arr := make([]int, 0, 5)
arr = append(arr, 1,2,3,4, 5)

在初始化[]int slice的时候在make中设置了cap的长度,就是slice的大小。

这两种方法对应的功能和输出结果是没有任何差别的,但是实际运行的时候,方法2会比少运行了一个growslice的命令。

 

如果不设置cap,不管是使用make,还是直接使用[]slice 进行初始化,编译器都会计算初始化所需的空间,使用最小化的cap进行初始化。

a := make([]int, 0) // cap 为0
a := []int{1,2,3} // cap 为3

 

切片作为函数参数

 

https://studygolang.com/articles/9876

golang提供了array和slice两种序列结构。其中array是值类型。slice则是复合类型。slice是基于array实现的。slice的第一个内容为指向数组的指针,然后是其长度和容量。通过array的切片可以出slice,也可以使用make创建slice,此时golang会生成一个匿名的数组。

因为slice依赖其底层的array,修改slice本质是修改array,而array又是有大小限制,当超过slice的容量,即数组越界的时候,需要通过动态规划的方式创建一个新的数组块。把原有的数据复制到新数组,这个新的array则为slice新的底层依赖。

数组还是切片,在函数中传递的不是引用,是另外一种值类型,即通过原始变量进行切片传入。函数内的操作即对切片的修改操作了。当然,如果为了修改原始变量,可以指定参数的类型为指针类型。传递的就是slice的内存地址。函数内的操作都是根据内存地址找到变量本身。

 

传引用方式
array和slice作为参数传递的过程基本上是一样的,即传递他们切片。有时候我们需要处理传递引用的形式。golang提供了指针很方便实现类似的功能。

func main() {
    slice := []int{0, 1}
    fmt.Printf("slice %v %p \n", slice, &slice)

    changeSlice(&slice)
    fmt.Printf("slice %v %p \n", slice, &slice)

    slice[1] = -1111

    fmt.Printf("slice %v %p \n", slice, &slice)
}

func changeSlice(s *[]int) {
    fmt.Printf("func s %v %p \n", *s, s)
    (*s)[0] = -1
    *s = append(*s, 3)
    (*s)[1] =  1111
}
输出如下:

slice [0 1] 0xc42000a1e0 
func s [0 1] 0xc42000a1e0 
slice [-1 1111 3] 0xc42000a1e0 
slice [-1 -1111 3] 0xc42000a1e0
从输出可以看到,传递给函数的是slice的指针,函数内对对s的操作本质上都是对slice的操作。并且也可以从函数内打出的s地址看到,至始至终就只有一个切片。虽然在append过程中会出现临时的切片或数组。

 

 

三 map初始化

 

// 1:先声明map 后make

var m1 map[string]string

// 再使用make函数创建一个非nil的map,nil map不能赋值

m1 = make(map[string]string)

(如果没有make会报错:

panic: assignment to entry in nil map

// 最后给已声明的map赋值

m1["a"] = "aa"

m1["b"] = "bb"

 

// 2:直接创建

m2 := make(map[string]string)

// 然后赋值

m2["a"] = "aa"

m2["b"] = "bb"

 

// 3:初始化 + 赋值一体化

m3 := map[string]string{

"a": "aa",

"b": "bb",

}

 

// ==========================================

// 查找键值是否存在

if v, ok := m1["a"]; ok {

fmt.Println(v)

} else {

fmt.Println("Key Not Found")

}

 

// 遍历map

for k, v := range m1 {

fmt.Println(k, v)

}

 

 

如下定义:

mapStr := make(map[string]string)
只能储存string类型的value。

如下定义:

mapInt := make(map[string]int)
只能保存int类型的value。

如下定义:

mapInterface := make(map[string]interface{})
可以保存string、int等不同类型的value。

mu := make([]map[string]interface{},0)
a1 := map[string]interface{} {"id": 1, "parentId": 0, "createTime": "2020-02-02T06:50:36.000+0000", "title": "商品", "level": 0,
"sort": 0, "name": "pms", "icon": "product", "hidden": 0}

a2 := map[string]interface{} {"id": 2, "parentId": 1, "createTime": "2020-02-02T06:51:50.000+0000", "title": "商品列表", "level": 1,
"sort": 0, "name": "product", "icon": "product-list", "hidden": 0}

mu = append(mu, a1)
mu = append(mu, a2)
————————————————
原文链接:https://blog.csdn.net/aaronthon/article/details/107777820

 

map存储函数:

type Msg func(name string) string

func main() {
    runtime.GOMAXPROCS(runtime.NumCPU())
    wg := &sync.WaitGroup{}
    c := make(chan os.Signal, 1)
    handleMap := make(map[int]Msg)
    handleMap[1] = handle1
    handleMap[2] = handle2
    handleMap[3] = handle3

 

 

golang empty map 和nil map区别

 

The zero value for a map type is nil, that is, a reference to no hash table at all. 这个nil map是不能添加元素的, 必须进行初始化才可以。 既然如此, 何不默认初始化为empty map呢? 不解!

对C++的NULL进行操作,总是让人提醒吊胆, 那么在nil map上操作, 会怎样呢? 继续看go圣经: Most operations on maps, including lookup, delete, len, and range loops, are safe to perform on a nil map reference, since it behaves like an emtpy map. But storing to a nil map cause a panic.
————————————————

var m map[int]int

if m == nil {
fmt.Println("it is nil, 3")
}else{
fmt.Println("it not not nil, 3")
}

fmt.Println("map len3 is", len(m))

m = make(map[int]int, 0) // 必要,否则panic

if m == nil {
fmt.Println("it is nil, 4")
}else{
fmt.Println("it not not nil, 4")
}

————————————————
参考:链接:https://blog.csdn.net/stpeace/article/details/82050482

 

结构体初始化

方式一:通过 var 声明结构体

在 Go 语言中当一个变量被声明的时候,系统会自动初始化它的默认值,比如 int 被初始化为 0,指针为 nil。
var 声明同样也会为结构体类型的数据分配内存,所以我们才能像上一段代码中那样,在声明了 var s T 之后就能直接给他的字段进行赋值

 

var s T
s.a = 5
s.b = 8

数组也可以看作是一种结构体类型,不过它使用下标而不是具名的字段

 

方式二:使用 new

使用 new 函数给一个新的结构体变量分配内存,它返回指向已分配内存的指针:var t *T = new(T)。

 

type struct1 struct {
    i1 int
    f1 float32
    str string
}

func main() {
    ms := new(struct1)
    ms.i1 = 10
    ms.f1 = 15.5
    ms.str= "Chris"

    fmt.Printf("The int is: %d\n", ms.i1)
    fmt.Printf("The float is: %f\n", ms.f1)
    fmt.Printf("The string is: %s\n", ms.str)
    fmt.Println(ms)
}

与面向对象语言相同,使用点操作符可以给字段赋值:structname.fieldname = value
同样的,使用点操作符可以获取结构体字段的值:structname.fieldname

 

方式三:使用字面量

type Person struct {
    name string
    age int
    address string
}

func main() {
    var p1 Person
    p1 = Person{"lisi", 30, "shanghai"}   //方式A
    p2 := Person{address:"beijing", age:25, name:"wangwu"} //方式B
    p3 := Person{address:"NewYork"} //方式C
}

在(方式A)中,值必须以字段在结构体定义时的顺序给出。(方式B)是在值前面加上了字段名和冒号,这种方式下值的顺序不必一致,并且某些字段还可以被忽略掉,就想(方式C)那样。
除了上面这三种方式外,还有一种初始化结构体实体更简短和常用的方式,如下:

ms := &Person{"name", 20, "bj"}
ms2 := &Person{name:"zhangsan"}

&Person{a, b, c} 是一种简写,底层仍会调用 new(),这里值的顺序必须按照字段顺序来写,同样它也可以使用在值前面加上字段名和冒号的写法(见上文的方式B,C)。

表达式 new(Type) 和 &Type{} 是等价的。

三,几种初始化方式之间的区别

到目前为止,我们已经了解了三种初始化结构体的方式:

//第一种,在Go语言中,可以直接以 var 的方式声明结构体即可完成实例化
var t T
t.a = 1
t.b = 2

//第二种,使用 new() 实例化
t := new(T)

//第三种,使用字面量初始化
t := T{a, b}
t := &T{} //等效于 new(T)

使用 var t T 会给 t 分配内存,并零值化内存,但是这个时候的 t 的类型是 T
使用 new 关键字时 t := new(T),变量 t 则是一个指向 T 的指针
从内存布局上来看,我们就能看出这三种初始化方式的区别:
使用 var 声明:

使用 new 初始化:

使用结构体字面量初始化:

 

https://www.cnblogs.com/liyutian/p/10050320.html

 

 

一般在进行例如type T struct {a, b int}的结构体定义之后
习惯使用t := new(T)给该结构体变量分配内存,它返回指向已分配内存的指针。变量t是一个指向T的指针,此时结构体字段的值是它们所属类型的零值。
声明 var t T 也会给 t 分配内存,并零值化内存,但是这个时候 t是类型T。在这两种方式中,t 通常被称做类型T的一个实例(instance)或对象(Object)

var t *T = new(T)

等价于t := new(T)

golang构造函数?

在Go语言中没有构造函数的概念,对象的创建通常交由一个全局的创建函数来完成,以 NewXXX来命名,表示“构造函数”:
这一切非常自然,开发者也不需要分析在使用了new之后到底背后发生了多少事情。在Go语言中,一切要发生的事情都直接可以看到。
—— 《Go语言编程》

func NewRect(x, y, width, height float64) *Rect { 
    return &Rect{x, y, width, height}
}

 

 

 

slice作为函数参数:

直接贴代码

复制代码
func sliceModify(slice []int) {
    // slice[0] = 88
    slice = append(slice, 6)
}
func main() {
    slice := []int{1, 2, 3, 4, 5}
    sliceModify(slice)
    fmt.Println(slice)
}
复制代码

 

返回的没变,坑爹的,这个设计太那啥了,可以正确跑出效果的版本如下:

复制代码
func sliceModify(slice *[]int) {
    *slice = append(*slice, 6)
}
func main() {
    slice := []int{1, 2, 3, 4, 5}
    sliceModify(&slice)
    fmt.Println(slice)
}

 map作为函数参数:

当你声明一个map的时候:

m := make(map[int]int)

编译器会调用 runtime.makemap

// makemap implements a Go map creation make(map[k]v, hint)
// If the compiler has determined that the map or the first bucket
// can be created on the stack, h and/or bucket may be non-nil.
// If h != nil, the map can be created directly in h.
// If bucket != nil, bucket can be used as the first bucket.
func makemap(t *maptype, hint int64, h *hmap, bucket unsafe.Pointer) *hmap

 

所以实际上是返回一个hmap的指针。

func main(){
    m := make(map[int]int)
    m[1] = 1
    fmt.Printf("原始map的内存地址是:%p\n", m)
    modify(m)
    fmt.Println("map值被修改了,新值为:", m)
}
 
func modify(m interface{}){
    fmt.Printf("函数里接收到map的内存地址是:%p\n", p)
    m := p.(map[int]int)
    m[1] = 2
}

 

输出结果:

  1.  
    原始map的内存地址是:0xc00009e030
  2.  
    函数里接收到map的内存地址是:0xc00009e030
  3.  
    map值被修改了,新值为: map[1:2]

在main函数中,m是个指针变量,它保存的值是:0xc00009e030。

在modify函数中,m也是个指针变量,保存的值也是:0xc00009e030。

说明初始化map后,返回的是指针变量,在函数之间,传递的是map的地址。

map和channel是相似的。

那么为什么不是 *map[key]value呢,这样不是更直观?

Ian Taylor answered this recently in a golang-nuts 原话是:

In the very early days what we call maps now were written as pointers, so you wrote *map[int]int. We moved away from that when we realized that no one ever wrote map without writing \*map.

意思是,一开始也是写作 *map[key]value,后来发现所有的map都是当作指针来用的,于是就省略简写了。

转载于:https://www.cnblogs.com/zhouj-happy/p/10962500.html

 

 

golang学习笔记 ---解析(map[string]interface{})数据格式

interface转其他类型 
有时候返回值是interface类型的,直接赋值是无法转化的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main
 
import (
    "fmt"
)
 
func main() {
 
    var interface{}
    var b string
    a = "123"
    //b = a //cannot use a (type interface {}) as type string in assignment: need type assertion
    b = a.(string)
    fmt.Println(b)
}

  

输出:

123

 

通过a.(string) 转化为string,通过a.(int)转化为类型。 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main
 
import (
    "fmt"
)
 
func main() {
 
    var interface{}
    var b string
    var c int
    a = "123"
    //b = a //cannot use a (type interface {}) as type string in assignment: need type assertion
    b = a.(string)
    fmt.Println(b)
    a = 124
    c = a.(int)
    fmt.Println(c)
 
}

可以通过a.(type)来判断a可以转为什么类型。

注意事项

  • map记得分配内存
  • 解析出来的int类型会变成float64类型
  • 注意判断不为nil后再转换类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package main
 
import (
    "encoding/json"
    "fmt"
)
 
func main() {
    //1.准备一段json
    b := []byte(`{"Name":"sary","Age":18}`)
    //2.声明空接口
    var interface{}
    //3.解析
    err := json.Unmarshal(b, &i)
    if err != nil {
        fmt.Println("json err:", err)
    }
    //自动转map
    fmt.Println(i)
    //4.使用interface的json,可以判断类型
    m := i.(map[string]interface{})
    for k, v := range m {
        switch value := v.(type) {
        case nil:
            fmt.Println(k, "is nil""null")
        case string:
            fmt.Println(k, "is string", value)
        case int:
            fmt.Println(k, "is int", value)
        case float64:
            fmt.Println(k, "is float64", value)
        case []interface{}:
            fmt.Println(k, "is an array:")
            for i, u := range value {
                fmt.Println(i, u)
            }
        case map[string]interface{}:
            fmt.Println(k, "is an map:")
        default:
            fmt.Println(k, "is unknown type", fmt.Sprintf("%T", v))
        }
    }
 
}

  

输出:

map[Age:18 Name:sary]

Name is string sary

Age is float64 18

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
package main
 
import (
    "encoding/json"
    "fmt"
)
 
func main() {
    var map[string]interface{}     //声明变量,不分配内存
    m = make(map[string]interface{}) //必可不少,分配内存
    m["name"] = "sary"
    var age int = 18
    m["age"] = age
    m["addr"] = "China"
    print_map(m)
    fmt.Println()
 
    data, err := json.Marshal(m)
    if err != nil {
        fmt.Println("err:", err)
    else {
        fmt.Println("data:", data)
 
    }
 
    m1 := make(map[string]interface{})
    err = json.Unmarshal(data, &m1)
    if err != nil {
        fmt.Println("err:", err)
    else {
        print_map(m1)
 
    }
 
    fmt.Println()
    value, ok := m1["key1"]
    if ok {
        fmt.Printf(value.(string))
    else {
        fmt.Println("key1 不存在")
    }
 
}
 
//解析 map[string]interface{} 数据格式
func print_map(m map[string]interface{}) {
 
    fmt.Println("enter print_map########### ")
    for k, v := range m {
        switch value := v.(type) {
        case nil:
            fmt.Println(k, "is nil""null")
        case string:
            fmt.Println(k, "is string", value)
        case int:
            fmt.Println(k, "is int", value)
        case float64:
            fmt.Println(k, "is float64", value)
        case []interface{}:
            fmt.Println(k, "is an array:")
            for i, u := range value {
                fmt.Println(i, u)
            }
        case map[string]interface{}:
            fmt.Println(k, "is an map:")
            print_map(value)
        default:
            fmt.Println(k, "is unknown type", fmt.Sprintf("%T", v))
        }
    }
    fmt.Println("out print_map ########### ")
}

  

输出:

enter print_map###########

name is string sary

age is int 18

addr is string China

out print_map ###########

 

data: [123 34 97 100 100 114 34 58 34 67 104 105 110 97 34 44 34 97 103 101 34 58 49 56 44 34 110 97 109 101 34 58 34 115 97 114 121 34 125]

enter print_map###########

addr is string China

age is float64 18

name is string sary

out print_map ###########

 

key1 不存在

 

 

golang中结构体的初始化方法(new方法)

 

 

自定义一个结构体

 

type Rect struct {

    x, y float64

    width, height float64

}

初始化方法:

 

rect1 := new(Rect)

rect2 := &Rect{}

rect3 := &Rect{0, 0, 100, 200}

rect4 := &Rect{width:100, height:200}

注意这几个变量全部为指向Rect结构的指针(指针变量),因为使用了new()函数和&操作符.而如果使用方法

 

a := Rect{}

则表示这个是一个Rect{}类型.两者是不一样的.参考代码:

func main() {

rect1 := &Rect{0, 0, 100, 200}

rect1.x = 10

 

a := Rect{}

a.x = 15

 

fmt.Printf("%v\n%T\n", a, a)

fmt.Printf("%v\n%T\n", rect1, rect1)

}

运行结果为:

 

{15 0 0 0}

 main.Rect

 &{10 0 100 200}

 *main.Rect

从结果中可以清楚的看到两者的不同.

 

在Go语言中,未进行初始化的变量都会被初始化为该类型的零值,例如bool类型的零值为false, int类型的零值为0, string类型的零值为空字符串. 在Go语言中没有构造函数的概念,对象的创建通常交由一个全局的创建函数来完成,以NewXXX来命令,表示"构造函数":

 

func NewRect(x ,y ,width, height float64) {

    return &Rect{x, y, width, height}

}

这一切非常自然.开发者也不需要分析在使用了new之后到底背后发生了多少事情.在Go语言中,一切要发生的事情都直接可以看到. 附:

 

用 new 分配内存 内建函数 new 本质上说跟其他语言中的同名函数功能一样:new(T) 分配了零值填充的 T 类型的内存空间,并且返回其地址,一个 *T 类型的值。用 Go 的术语说,它返回了一个指针,指向新分配的类型 T 的零值。记住这点非常重要。 这意味着使用者可以用 new 创建一个数据结构的实例并且可以直接工作。如 bytes.Buffer的文档所述 “Buffer 的零值是一个准备好了的空缓冲。” 类似的,sync.Mutex 也没有明确的构造函数或 Init 方法。取而代之,sync.Mutex 的零值被定义为非锁定的互斥量。 零值是非常有用的。例如这样的类型定义,56 页的”定义自己的类型” 内容。 ===================

 

必记得 make 仅适用于 map,slice 和 channel,并且返回的不是指针。应当用 new获得特定的指针。

 

map判断key是否存在

if _, ok := map[key]; ok {
    // 存在
}
 
if _, ok := map[key]; !ok {
    // 不存在
}

 

    // Go 语言中 range 关键字用于for循环中迭代数组(array)、切片(slice)、
    // 链表(channel)或集合(map)的元素。在数组和切片中它返回元素的索引值,
    // 在集合中返回 key-value 对的 key 值。


    //range是取出两值


    nums := []int{2, 3, 4}
    sum := 0
    for _, num := range nums {
        sum += num
    }
    fmt.Println("sum:", sum)
    //在数组上使用range将传入index和值两个变量。
    // 上面那个例子我们不需要使用该元素的序号,所以我们使用空白符"_"省略了。
    // 有时侯我们确实需要知道它的索引。
    for i1, num1 := range nums {
        if num1 == 3 {
            fmt.Println("index:", i1) //索引从0开始
        }
    }


    //range也可以用在map的键值对上。
    kvs := map[string]string{"a": "apple", "b": "banana"}
    for k, v := range kvs {
        fmt.Printf("%s -> %s\n", k, v)
    }
    //range也可以用来枚举Unicode字符串。第一个参数是字符的索引,第二个是字符(Unicode的值)本身。
    for i2, c := range "go" {
        fmt.Println(i2, c)//得出的c是ASCII值
    }

 

 map和切片的组合定义

元素为map的slice

var userInfo = make([]map[string]string, 2, 2)
    if userInfo[0] == nil { //map 的默认值是nil
        userInfo[0] = make(map[string]string)
        userInfo[0]["name"] = "lili"
        userInfo[0]["age"] = "20"
        userInfo[0]["sex"] = ""

    }
    if userInfo[1] == nil{
        userInfo[1] = make(map[string]string)
        userInfo[1]["name"] = "liwang"
        userInfo[1]["age"] = "30"
        userInfo[1]["sex"] = ""
    }

元素为切片的map

    var studentData = make(map[string][]string)
    studentData["age"] = []string{
        "12",
        "13",
        "15",
    }
    studentData["name"] = []string{
        "xiaomi",
        "xiaoli",
        "xiaowang",
    }
    studentData["sex"] = []string{
        "",
        "",
        "",
    }
    fmt.Println(studentData) //map[age:[12 13 15] name:[xiaomi xiaoli xiaowang] sex:[女 女 男]]

小应用:统计单词出现的次数:

    var str = "hello world hello world do do do"
    var strSlice = strings.Split(str," ")
    var mapStr  = make(map[string]int)
    for _, v := range strSlice {
        mapStr[v]++
    }
    fmt.Println(mapStr) // map[do:3 hello:2 world:2]