zoukankan      html  css  js  c++  java
  • golang(4):函数 & 数组 & 切片 & map & 锁

    内置函数

    // 1. close:主要用来关闭channel
    // 2. len:用来求长度,比如string、array、slice、map、channel
    // 3. new:用来分配内存,主要用来分配值类型,比如int、struct。返回的是指针
    // 4. make:用来分配内存,主要用来分配引用类型,比如chan、map、slice
    // 5. append:用来追加元素到数组、slice中

    示例如下:

    // new 示例:
    package main
    
    import "fmt"
    
    func main(){
        var i int
        fmt.Println(i)
    
        j := new(int)
        fmt.Println(j)
        
        *j = 100
        fmt.Println(*j)
    }
    
    // 运行结果如下: 
    [root@NEO example01_new]# go run main/main.go 
    0
    0xc0000160b0
    100
    [root@NEO example01_new]#
    
    // append 示例:
    package main
    
    import "fmt"
    
    func main(){
        var a []int    // [] 中不写数字表示是一个切片 slice
        a = append(a,1,2)    // append 中也是可变参数
        fmt.Println(a)
    
        a = append(a,a...)    // 添加一个切片;a...表示 切片a 中的所有元素
        fmt.Println(a)
    }
    
    // 运行结果如下:
    [root@NEO example01_append]# go run main/main.go
    [1 2]
    [1 2 1 2]
    [root@NEO example01_append]# 
    
    // recover 示例:
    package main
    
    import "fmt"
    
    func test(){
        defer func(){   // func(){} 是匿名函数; func(){...}()  执行匿名函数
        if err := recover(); err != nil{    // recover()  是捕获异常
            fmt.Println(err)
        }
        }()
    
        a := 0
        b := 1/a
        fmt.Println(b)
        return
    }
    
    func main(){
        test()
        fmt.Println("hello world")
    }
    
    // 运行结果如下:
    [root@NEO example01_recover]# go run main/main.go
    runtime error: integer divide by zero
    hello world
    [root@NEO example01_recover]# 

    递归函数

    一个函数调用自己,就叫做递归。示例代码如下:

    // 示例代码1:
    package main
    
    import (
        "fmt"
    )
    
    func calc(n int) int {
        if n == 1 {
            return 1
        }
    
        return calc(n-1) * n
    }
    
    func main() {
        n := calc(5)
        fmt.Println(n)
    }
    
    
    // 示例2:斐波那契数
    package main
    
    import "fmt"
    
    func fab(n int) int {
        if n <= 1 {
        return 1
        }
        return fab(n-1)+fab(n-2)
    }
    
    func main(){
        n := fab(10)
        fmt.Println(n)
    }

    递归的设计原则

    1)一个大的问题能够分解成相似的小问题
    2)定义好出口条件

    闭包

    闭包:一个函数和与其相关的引用环境组合而成的实体。示例如下:

    // 示例代码:
    package main
    
    import "fmt"
    
    func Adder() func(int) int {    // 函数 Adder 的返回值是也一个函数
        var x int
        return func(d int) int {    // 该处的 return 要和上面定义的返回值类型的 return 的函数一样
        x += d
        return x
        }
    }
    
    func main() {
        f := Adder()   // f 是一个函数
        fmt.Println("f(1)",f(1))         // 此时 x 的值为默认的 0
        fmt.Println("f(10)",f(10))    // 此时 x 的值变成了 1
        fmt.Println("f(100)",f(100))    // 此时 x 的值变成了 10
    }
    
    // 运行结果如下:
    [root@NEO example03_closure]# go run main/main.go
    f(1) 1
    f(10) 11
    f(100) 111
    [root@NEO example03_closure]#

    数组和切片

    // 1. 数组:是同一种数据类型的固定长度的序列。
    // 2. 数组定义:var a [len]int,比如:var a[5]int,一旦定义,长度不能变;元素的值默认为0
    // 3. 长度是数组类型的一部分,因此,var a[5] int和var a[10]int是不同的类型
    // 4. 数组可以通过下标进行访问,下标是从0开始,最后一个元素下标是:len-1
           for i := 0; i < len(a); i++ {
           }
           
           for index, v := range a {
           }
    
    // 5. 访问越界,如果下标在数组合法范围之外,则触发访问越界,会panic
    // 6. 数组是值类型,因此改变副本的值,不会改变本身的值
        arr2 := arr1
        arr2[2] = 100

    数组的初始化

    1. var age0 [5]int = [5]int{1,2,3}        // 定义的时候初始化
    2. var age1 = [5]int{1,2,3,4,5}
    3. var age2 = […]int{1,2,3,4,5,6}        // ... 的作用:编译器会自动帮你数里面有几个元素,然后就是长度为多少的数组
    4. var str = [5]string{3:”hello world”, 4:”tom”}    // 定义时指定下标对应的元素

    多维数组

    1. var age [5][3]int        // 二维数组:可理解成5行3列
    2. var f [2][3]int = [...][3]int{{1, 2, 3}, {7, 8, 9}}        // ... 表示 编译器自己去数有多少行

    多维数组遍历

    // 示例代码:
    package main
    
    import "fmt"
    
    func test(){
        var arr [2][5]int = [...][5]int{{1,2,3,4,5},{6,7,8,9,10}}
    
        for row,v_row := range arr{
        for col,v_col := range v_row {
            fmt.Printf("(%d,%d)=%d ",row,col,v_col)
        }
        fmt.Println()
        }
    }
    
    func main(){
        test()
    }
    
    
    // 运行结果如下:
    [root@NEO example04_multi_dim_arr]# go run main/main.go
    (0,0)=1 (0,1)=2 (0,2)=3 (0,3)=4 (0,4)=5 
    (1,0)=6 (1,1)=7 (1,2)=8 (1,3)=9 (1,4)=10 
    [root@NEO example04_multi_dim_arr]#

    切片

    // 1. 切片:切片是数组的一个引用,因此切片是引用类型;把一个切片传入函数中修改切片中元素的值,那么该切片在函数外的值也会改变,数组则不会
    // 2. 切片的长度可以改变,因此,切片是一个可变的数组
    // 3. 切片遍历方式和数组一样,并且可以用len()求长度
    // 4. cap可以求出slice最大的容量,0 <= len(slice) <= cap(array),其中array是slice引用的数组
    // 5. 切片的定义:var 变量名 []类型,比如 var str []string 和 var arr []int (这种方式只是定义了切片,但并没有对切片初始化,所以此时不能直接 arr[0]=1 这样赋值)

    示例如下:

    // 切片是引用类型的示例1:
    package main
    
    import "fmt"
    
    func modifySlice(a []int){    // 该函数的需要传入的参数是切片类型;slice map 和 chan 类型的参数传入的就是地址
        a[1] = 100        // 修改 切片 a 的元素的值后,函数外 a 的值也会改变
    }
    
    func testSlice(){
        var s []int = []int{1,2,3}
        modifySlice(s)    // 切片是引用类型,是数组的一个引用(可理解成切片是一个地址);此函数传了一个引用(引用中包含指针);可理解成给该函数传了一个地址
        fmt.Println(s)
    }
    
    func main(){
        testSlice()
    }
    
    
    // 运行结果如下:
    [root@NEO example05_slice_ref_type]# go run main/main.go
    [1 100 3]        // 函数外切片的值也发生了改变
    [root@NEO example05_slice_ref_type]#
    
    // 切片是引用类型的示例2:
    package main
    
    import "fmt"
    
    func testSliceRefArr(){
        var a = [10]int{1,2,3}    // 声明并初始化一个数组
        b := a[1:4]        // 声明并初始化一个切片
    
        fmt.Printf("%p
    ",b)    // %p 表示十六进制表示,可用于打印一个地址
        fmt.Printf("%p
    ",&a[1])    // 数组 a 第一个元素的地址
    }
    
    func main(){
        testSliceRefArr()
    }
    
    // 运行结果如下:
    [root@NEO example05_slice_ref_arr]# go run main/main.go
    0xc000064008
    0xc000064008    // 切片 b  的地址和 数组a第一个元素的地址是一样的;所以 切片是一个地址
    [root@NEO example05_slice_ref_arr]# 
    
    // 示例代码:
    package main
    
    import "fmt"
    
    func testSlice(){
        var slice []int    // 声明一个数组;只是定义了数组,但并没有对切片初始化,所以此时不能直接 arr[0]=1 这样赋值
        var arr [5]int = [...]int{1,2,3,4,5}    // 声明并初始化一个数组
    
        slice = arr[2:5]    // 对切片初始化; 顾首不顾尾;可简写成 arr[2:]
        fmt.Println(slice)
        fmt.Println("len:",len(slice))
        fmt.Println("cap:",cap(slice))
    
        slice = slice[0:1]
        fmt.Println("len:",len(slice))
        fmt.Println("cap:",cap(slice))
    }
    
    func main(){
        testSlice()
    }
    // slice、map、channel 都可以用 make 来初始化
    
    // 运行结果如下:
    [root@NEO example05_slice01]# go run main/main.go
    [3 4 5]
    len: 3
    cap: 3
    len: 1
    cap: 3
    [root@NEO example05_slice01]#

    切片的创建方式1:通过 数组 创建

    // 1. 切片初始化:var slice []int = arr[start:end] ;包含start到end之间的元素,但不包含end 
    // 2. var slice []int = arr[0:end]可以简写为 var slice []int=arr[:end]
    // 3. var slice []int = arr[start:len(arr)] 可以简写为 var slice[]int = arr[start:]
    // 4. Var slice []int = arr[0, len(arr)] 可以简写为 var slice[]int = arr[:]
    // 5. 如果要切片最后一个元素去掉,可以这么写: 
           Slice = slice[:len(slice)-1]

    切片的创建方式2: 通过make来创建切片

    var slice []type = make([]type, len)
    slice  := make([]type, len)
    slice  := make([]type, len, cap)

    用append内置函数操作切片

    // 示例:
    slice = append(slice, 10)
    var a = []int{1,2,3}
    var b = []int{4,5,6}
    a = append(a, b…)    // b... 表示 切片 b 中的所有元素
    
    
    // append 的示例:
    package main
    
    import "fmt"
    
    func testSlice(){
        var a [5]int = [...]int{1,2,3,4,5}
        b := a[1:]
    
        fmt.Printf("b=%p a[1]=%p
    ",b,&a[1])   // 此时切片 b 的地址和 数组a第一个元素的地址是一样的
    
        b[1] = 100
        fmt.Println("before a:",a)    // 此时 改变切片 b 的值 数组a 也会改变
    
        b = append(b,10)
        b = append(b,10)
        b = append(b,10)
        b = append(b,10)
        b = append(b,10)
        b = append(b,10)    // 添加了5个元素后,已经超过了切片 b 的容量
    
        fmt.Println(b)
        fmt.Printf("b=%p a[1]=%p
    ",b,&a[1])    // 此时切片 b 的地址和 数组a第一个元素的地址就不再一样;因为在声明并定义切片b时没有超过b的容量,此时b指向原来的数组,当超过b的容量后,系统会重新开辟一块内存,并把切片b原有的值放到新开辟的内存中,然再把追加的内容放进去
        
        b[1] = 1000
        fmt.Println("after a:",a)    // 此时改变 b 值, a的值将不会改变,因为 切片 b 指向的内存地址已经不是 a 了
    }
    
    func main(){
        testSlice()       // 切片是可变的,就是因为内部会根据容量的大小重新分配内存
    }
    
    // 运行结果如下:
    [root@NEO example05_slice_beyond_cap]# go run main/main.go
    b=0xc000014158 a[1]=0xc000014158
    before a: [1 2 100 4 5]                // a 中对应的值也发生了改变
    [2 100 4 5 10 10 10 10 10 10]
    b=0xc000062080 a[1]=0xc000014158
    after a: [1 2 100 4 5]                // 此时 a 中的值并没有改变
    [root@NEO example05_slice_beyond_cap]# 

    切片resize

    var a = []int {1,3,4,5}
    b := a[1:2]
    b = b[0:3]

    切片拷贝:copy()

    s1 := []int{1,2,3,4,5}
    s2 := make([]int, 10)
    copy(s2, s1)    // 把 切片 s1 拷贝到 切片 s2 中;拷贝不会扩容
    
    // 示例代码:
    package main
    
    import "fmt"
    
    func testCopy(){
        var a []int = []int{1,2,3,4,5}
        b := make([]int,10)
        c := make([]int,1)
    
        copy(b,a)    // 把a拷贝到b中
        copy(c,a)    // 把a拷贝到c中
    
        fmt.Println(b)
        fmt.Println(c)    // 拷贝不会扩容
    }
    
    func main(){
        testCopy()
    }
    
    // 运行结果如下:
    [root@NEO example05_slice_copy]# go run main/main.go
    [1 2 3 4 5 0 0 0 0 0]
    [1]
    [root@NEO example05_slice_copy]# 

    切片示例:使用非递归的方式实现斐波那契数列,打印前10个数。

    // 示例代码如下:
    package main
    
    import "fmt"
    
    func fab(n int){
        var a []int           // 声明一个切片;[]中没有数字,所以是切片
        a = make([]int,n)    // 为切片a 分配内存
    
        a[0] = 1
        a[1] = 1
    
        for i := 2; i < len(a); i++ {
        a[i] = a[i-1] + a[i-2]
        }
        
        for _,v := range a{
        fmt.Println(v)
        }
    }
    
    func main() {
        fab(10)
    }
    
    // 运行结果如下:
    [root@NEO example04_fab_slice]# go run main/main.go
    1
    1
    2
    3
    5
    8
    13
    21
    34
    55
    [root@NEO example04_fab_slice]#

    string与slice

    string底层就是一个byte的数组,因此,也可以进行切片操作
    str := “hello world”
    s1 := str[0:5]
    fmt.Println(s1)
    
    s2 := str[5:]
    fmt.Println(s2)
    
    string本身是不可变的,因此要改变string中字符,需要如下操作:先把字符串转换成数组
    
    str := “hello world”
    s := []byte(str)
    s[0] = ‘o’
    str = string(s)

    示例代码:

    // 示例代码如下:
    package main
    
    import "fmt"
    
    func strModifyByte(){
        str := "hello world"
        s := []byte(str)    // 把字符串转换为 byte 数组
        fmt.Println(s)
    
        s[0] = 'o'
        str = string(s)    // 再把 byte 数组转换为字符串
        fmt.Println(str)
    }
    
    func strChangeRune(){
        str := "hello 中国"
        s := []rune(str)    // 把字符串转换为 字符 数组 (可以转换中文)
        fmt.Println(s)
    
        s[5] = ''        // 字符类型 一定要用 单引号
        str = string(s)    // 把 字符数组 转换成字符串
        fmt.Println(str)
    }
    
    func main(){
        strModifyByte()
        strChangeRune()
    }
    
    
    // 运行结果如下:
    [root@NEO example05_string_change]# go run main/main.go
    [104 101 108 108 111 32 119 111 114 108 100]
    oello world
    [104 101 108 108 111 32 20013 22269]
    hello大中国
    [root@NEO example05_string_change]# 

    排序和查找操作

    // 排序操作主要都在 sort包中,导入就可以使用了
        import(“sort”)
    // sort.Ints对整数进行排序, sort.Strings对字符串进行排序, sort.Float64s对浮点数进行排序.
    
    // 查找
    sort.SearchInts(a []int, b int) 从数组a中查找b,前提是a必须有序
    sort.SearchFloats(a []float64, b float64) 从数组a中查找b,前提是a必须有序
    sort.SearchStrings(a []string, b string) 从数组a中查找b,前提是a必须有序

    示例代码:

    // 示例代码:
    package main
    
    import (
        "fmt"
        "sort"
    )
    
    func testSortInt(){
        var a = [...]int{1, 8, 38, 2, 348, 484}
        sort.Ints(a[:])    // 数组是值类型,所以对数组排序时需要传入其切片
        fmt.Println(a)
    }
    
    func testSortStrings(){
        var a = [...]string{"abc", "efg", "b", "A", "eeee"}
        sort.Strings(a[:])
        fmt.Println(a)
    }
    
    func testSortFloat(){
        var a = [...]float64{2.3, 0.8, 28.2, 392342.2, 0.6}
        sort.Float64s(a[:])
        fmt.Println(a)
    }
    
    func testSearchInts(){
        var a = [...]int{1, 8, 38, 2, 348, 484}
        sort.Ints(a[:])    // 搜索前需要先将数组排序;如果没有先排序,搜索到的索引值也是排序后的索引值
        index := sort.SearchInts(a[:],348)        // 传入切片
        fmt.Println(index)
    }
    
    
    func main(){
        testSortInt()
        testSortStrings()
        testSortFloat()
        testSearchInts()
    }
    
    // 运行结果如下:
    [root@NEO example05_sort_search]# go run main/main.go
    [1 2 8 38 348 484]
    [A abc b eeee efg]
    [0.6 0.8 2.3 28.2 392342.2]
    4
    [root@NEO example05_sort_search]# 

     

    map数据结构

    map简介:key-value的数据结构,又叫字典或关联数组
    map 声明:
    var map1 map[keytype]valuetype
    var a map[string]string
    var a map[string]int
    var a map[int]string
    var a map[string]map[string]string        // 声明是不会分配内存的,初始化需要make
    
    
    map相关操作
    var a map[string]string = map[string]string{“hello”: “world”}
    a = make(map[string]string, 10)
    a[“hello”] = "world"    // 插入和更新
    val, ok := a[“hello”]    // 查找; val 和 ok 都是变量名
    for k, v := range a {    // 遍历
    fmt.Println(k,v)
    }
    delete(a, “hello”)        // 删除 "hello" 这个key 对应的 k-v;删除map所有元素可以用  for 循环
    len(a)        // 长度

    示例代码:

    // 示例代码:
    package main
    
    import "fmt"
    
    func testMap(){
        var a map[string]string    // 声明、定义一个map;声明是不会分配内存的,初始化需要make(初始化会分配内存)
        a = make(map[string]string,10)    // 初始化
        a["a"] = "NEO"    // 未初始化的 map 不能直接使用(如这一步的赋值操作)
        fmt.Println(a)
    
        var b map[string]string = map[string]string{    // 声明并初始化
        "b":"neo",
        }
        fmt.Println(b)
    
        c := make(map[string]string,3)    // 声明并初始化
        fmt.Println(c)
    
        e := map[string]string{
        "key":"val",
        }
        fmt.Println(e)
        
        d := make(map[string]map[string]string,100)     // map 中嵌套 map;此处的map声明并初始化只是对最外层的 map 进行了 初始化,里面的 map 只进行了声明,但并未初始化
        d["key1"] = make(map[string]string,100)    // 在对里面那个 map 进行操作之前,需要先对里面的 map 初始化
        d["key1"]["key2"] = "abc"    // 对里面的map赋值
        d["key1"]["key3"] = "de"
        fmt.Println(d)
    
    }
    
    func testMapSearch(a map[string]map[string]string){
        val,is_existed := a["zhangsan"]    // 在 map a 中查找 "zhangsan" 这个key 是否存在;存在的话把"zhangsan"对应的值返回val,is_existed 为 true,不存在的话, is_existed 为 false
        fmt.Println(val,is_existed)
    
        if is_existed {
        val["nickname"] = "胖哥"
          val["passwd"] = "123456"
        } else {
        a["zhangsan"] = make(map[string]string)       // map 中不存在 "zhangsan" 这个 key,则在对其操作前要先对其初始化
        a["zhangsan"]["nickname"] = "胖哥"
            a["zhangsan"]["passwd"] = "123456"
        }
    }
    
    func main(){
        testMap()
        
        a := make(map[string]map[string]string,100)
        testMapSearch(a)
        fmt.Println("testMapSearch:",a)
    }
    // 值类型的数据定义(声明)之后就能直接使用,但 map 这种引用类型的数据定义之后一定要初始化之后才能使用
    
    // 运行结果如下:
    [root@NEO example06_map_var]# go run main/main.go
    map[a:NEO]
    map[b:neo]
    map[]
    map[key:val]
    map[key1:map[key2:abc key3:de]]
    map[] false
    testMapSearch: map[zhangsan:map[nickname:胖哥 passwd:123456]]
    [root@NEO example06_map_var]# 

    map是引用类型

    func modify(a map[string]int) {
        a[“one”] = 134        // 函数外 a 的值也会改变
    }

    slice of map

    items := make([]map[int][int], 5)
    For i := 0; i < 5; i++ {
        items[i] = make(map[int][int])        // 全部初始化;也可以用到哪个的时候再对哪个初始化
    }
    
    // map 和 slice 等是否为空用 nil 来判断

    map排序

    a. 先获取所有key,把key进行排序 (// 每次遍历时,map 的 key的顺序都是不一样的 (key是无序的))
    b. 按照排序好的key,进行遍历

    示例代码:

    // 示例代码:
    package main
    
    import (
        "fmt"
        "sort"
    )
    
    func testMapSort(){
        var a map[int]string
        a = make(map[int]string,100)
    
        a[2] = "hello"
        a[1] = "中国"
        a[25] = "aaa"
        a[8] = "abc"
        a[72] = "world"
    
        // 先获取所有key,把key进行排序
        var keys []int
        for k,_ := range a{
        keys = append(keys,k)
        }
        sort.Ints(keys)
    
        // 按照排序好的key,进行遍历
        for _,v := range keys{
        fmt.Println(v,a[v])
        }
    }
    
    func main(){
        testMapSort()
    }
    
    // 运行结果如下:
    [root@NEO example06_map_sort]# go run main/main.go
    1 中国
    2 hello
    8 abc
    25 aaa
    72 world
    [root@NEO example06_map_sort]# 

    Map反转

    // 初始化另外一个map,把key、value互换即可

    a. import(“sync”)
    b. 互斥锁, var mu sync.Mutex
    c. 读写锁, var mu sync.RWMutex
    
    // 互斥锁:同一时间只有一个 goroutine 能进去 (不论读写);适用于 写操作多的场景
    // 读写锁:多个 goroutine 可同时进行读操作(一个协程进去加了个读锁,后面的读协程也可以进去,但写协程进不去),但写协程还是互斥的;适用于 读多写少的场景

    示例代码:

    // 示例代码:
    package main
    
    import (
        "fmt"
        "math/rand"
        "sync"
        "sync/atomic"
        "time"
    )
    
    var lock sync.Mutex        // 互斥锁
    var rwLock sync.RWMutex        // 读写锁
    
    func testMap() {
        var a map[int]int
        a = make(map[int]int, 5)
    
        a[8] = 10
        a[3] = 10
        a[2] = 10
        a[1] = 10
        a[18] = 10
    
        for i := 0; i < 2; i++ {
            go func(b map[int]int) {    // 匿名函数
                lock.Lock()    
                b[8] = rand.Intn(100)    // 写操作;中间的代码加锁
                lock.Unlock()
            }(a)
        }
    
        lock.Lock()
        fmt.Println(a)        // 读操作;中间的代码加锁
        lock.Unlock()
    
        time.Sleep(time.Second)
    }
    
    func testRWLock() {
        var a map[int]int
        a = make(map[int]int, 5)
        var count int32
        a[8] = 10
        a[3] = 10
        a[2] = 10
        a[1] = 10
        a[18] = 10
    
        for i := 0; i < 2; i++ {
            go func(b map[int]int) {
                rwLock.Lock()
                b[8] = rand.Intn(100)
                time.Sleep(10 * time.Millisecond)
                rwLock.Unlock()
            }(a)
        }
    
        for i := 0; i < 100; i++ {
            go func(b map[int]int) {
                for {
                    rwLock.RLock()
                    time.Sleep(time.Millisecond)
                    //fmt.Println(a)
                    rwLock.RUnlock()
                    atomic.AddInt32(&count, 1)        // 修改公共数据 count : 进行原子性 写操作
                }
            }(a)
        }
        time.Sleep(time.Second * 3)
        fmt.Println(atomic.LoadInt32(&count))        // 读取进行原子性的数据
    }
    
    func main() {
        //testMap()
        testRWLock()
    }
  • 相关阅读:
    Android App常规测试内容
    腾讯的专项测试之道
    PyCharm Python迁移项目
    互联网架构的演变
    unittest最详细的解说
    (转)Python开发规范
    Python3.0+Selenium3进行Web自动化遇到的坑
    测试开发之路--英雄迟暮,我心未老
    团队作业9——事后分析(Beta版本)
    团队作业8--测试与发布(Beta阶段)
  • 原文地址:https://www.cnblogs.com/neozheng/p/11235338.html
Copyright © 2011-2022 走看看