• Go语言学习之4 递归&闭包&数组切片&map&锁


    主要内容:

    1. 内置函数、递归函数、闭包
    2. 数组与切片
    3. map数据结构
    4. package介绍

    5. 排序相关

    1. 内置函数、递归函数、闭包

    1)内置函数

          (1). close:主要用来关闭channel

                1). close函数是一个内建函数,用来关闭channel,这个channel要么是双向的, 要么是只写的(chan<- Type)。
                2). 这个方法应该只由发送者调用, 而不是接收者。
                3). 当最后一个发送的值都被接收者从关闭的channel(下简称为c)中接收时,接下来所有接收的值都会非阻塞直接成功,返回channel元素的零值。

                channel(Go中channel可以是只读、只写、同时可读写的)
                //定义只读的channel
                read_only := make(<-chan int)
                //定义只写的channel
                write_only := make(chan<- int)
                //可同时读写
                read_write := make(chan int)
                定义只读和只写的channel意义不大,一般用于在参数传递中,见代码:

     1 package main
     2 
     3 import (
     4     "fmt"
     5     "time"
     6 )
     7 
     8 func main() {
     9     c := make(chan int)
    10     go send(c)
    11     go recv(c)
    12     time.Sleep(3 * time.Second) //等待上面执行结束
    13 }
    14 //只能向chan里写数据
    15 func send(c chan<- int) {
    16     fmt.Println("write data to chan")
    17     for i := 0; i < 10; i++ {
    18         c <- i
    19     }
    20 }
    21 //只能取channel中的数据
    22 func recv(c <-chan int) {
    23     fmt.Println("read data from chan")
    24     for i := range c {
    25         fmt.Println(i)
    26     }
    27 }
    channel示例

                close函数简介:

    1) close函数是一个内建函数, 用来关闭channel,这个channel要么是双向的, 要么是只写的(chan<- Type)。 
    2) 这个方法应该只由发送者调用, 而不是接收者。 
    3) 当最后一个发送的值都被接收者从关闭的channel(下简称为c)中接收时, 接下来所有接收的值都会非阻塞直接成功,返回channel元素的零值。 

    例如如下的代码: 

    如果c已经关闭(c中所有值都被接收), x, ok := <- c, 读取ok将会得到false。

     1 package main
     2 
     3 import "fmt"
     4 
     5 func main() {
     6     ch := make(chan int, 5)
     7 
     8     for i := 0; i < 5; i++ {
     9         ch <- i
    10     }
    11 
    12     close(ch) // 关闭ch
    13     for i := 0; i < 10; i++ {
    14         e, ok := <-ch  //如果c已经关闭(c中所有值都被接收),再次读取 x, ok := <- c, 读取ok将会得到false
    15         fmt.Printf("%v, %v
    ", e, ok)
    16 
    17         if !ok {
    18             break
    19         }
    20     }
    21 }
    22 
    23 // 输出结果:
    24 // 0, true
    25 // 1, true
    26 // 2, true
    27 // 3, true
    28 // 4, true
    29 // 0, false
    View Code

     close函数使用注意事项:

    对于值为nil的channel或者对同一个channel重复close,都会panic,关闭只读channel会报编译错误。 
     1 1) 关闭值为nil的通道
     2 var c4 chan int
     3 // 运行时错误:panic: close of nil channel
     4 close(c4)
     5 
     6 2) 重复关闭同一个通道
     7 c3 := make(chan int, 1)
     8 close(c3)
     9 // 运行时错误:
    10 // panic: close of closed channel
    11 close(c3)
    12 
    13 
    14 3) 关闭只读通道
    15 c3 := make(<-chan int, 1)
    16 // 编译错误:
    17 // invalid operation: close(c3) (cannot close receive-only channel)
    18 close(c3)
    19 
    20 //正确的用法
    21 c1 := make(chan int, 1) // 双向通道 (bidirectional)
    22 c2 := make(chan<- int, 1) // 只写的 (send-only)
    23 close(c1)
    24 close(c2)
    close函数使用注意事项 

         close的详细使用见链接: https://www.jianshu.com/p/d24dfbb33781 

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

     1 package main
     2 
     3 import "fmt"
     4 
     5 func main() {
     6     var a []int
     7     a = append(a, 10, 20, 30)
     8     a = append(a, a...)
     9     fmt.Println(a) //[10 20 30 10 20 30]
    10 
    11     var b []int
    12     b = make([]int, 5) //存放5个int型数
    13     b = append(b, 10, 20, 30)
    14     b = append(b, b...)  
    15     fmt.Println(b) //b扩容了,[0 0 0 0 0 10 20 30 0 0 0 0 0 10 20 30]
    16 }
    append使用

         (6). panic和recover:用来做错误处理

     1 panic:
     2 1、内建函数
     3 2、假如函数F中书写了panic语句,会终止其后要执行的代码,在panic所在函数F内如果存在要执行的defer函数列表,按照defer的逆序执行
     4 3、返回函数F的调用者G,在G中,调用函数F语句之后的代码不会执行,假如函数G中存在要执行的defer函数列表,按照defer的逆序执行,这里的defer 有点类似 try-catch-finally 中的 finally
     5 4、直到goroutine整个退出,并报告错误
     6 
     7 recover:
     8 1、内建函数
     9 2、用来控制一个goroutine的panicking行为,捕获panic,从而影响应用的行为
    10 3、一般的调用建议
    11 a). 在defer函数中,通过recever来终止一个gojroutine的panicking过程,从而恢复正常代码的执行
    12 b). 可以获取通过panic传递的error
    panic及recover介绍
     1 package main
     2 
     3 import "fmt"
     4 import "time"
     5 import "errors"
     6 
     7 func initConfig() (err error) {
     8     return errors.New("init config failed")
     9 }
    10 
    11 func test() {
    12     defer func() {
    13         if err := recover(); err != nil {
    14             fmt.Println(err)
    15         }
    16     }() //执行匿名函数
    17 
    18     err := initConfig()
    19     if err != nil {
    20         panic(err) //在该处抛出一个panic的异常,然后在上面的defer中通过recover捕获这个异常,然后正常处理。
    21     }
    22 
    23     fmt.Println("can not excute") //该行及以下的代码不会被执行
    24     return
    25 }
    26 
    27 func main() {
    28     for {
    29         test()
    30         time.Sleep(time.Second)
    31     }
    32 }
    panic及recover使用

        总结:go中可以抛出一个panic的异常,然后在defer中通过recover捕获这个异常,然后正常处理。

         (7). new和make的区别
         new 的作用是初始化一个指向类型的指针(*T),make 的作用是为 slice,map 或 chan 初始化并返回引用(T)。

        例如:

     1 package main
     2 
     3 import "fmt"
     4 
     5 func test() {
     6 
     7     s1 := new([]int)
     8     fmt.Println(s1)  //&[]
     9 
    10     s2 := make([]int, 10)
    11     fmt.Println(s2)  // [0 0 0 0 0 0 0 0 0 0]
    12 
    13     *s1 = make([]int, 5)
    14     (*s1)[0] = 100
    15     s2[0] = 100
    16     fmt.Println(s1)  // &[100 0 0 0 0]
    17     return
    18 }
    19 
    20 func main() {
    21     test()
    22 }
    new和make的区别

    更详细的区别见链接: https://www.jb51.net/article/126703.htm

    2)递归函数

    递归定义:一个函数调用自己,就叫做递归。

    例1:计算一个数的阶乘

     1 package main
     2 
     3 import "fmt"
     4 
     5 func calcRecur(n int) int {
     6     if n == 1 {
     7         return 1
     8     }
     9 
    10     return calcRecur(n-1)*n
    11 }
    12 
    13 func main() {
    14     res := calcRecur(5)
    15     fmt.Println(res) //120
    16 }
    example

    例2:计算斐波那契数列

     1 package main
     2 
     3 import "fmt"
     4 
     5 func calcFib(n int) int {
     6     if n <= 1 {
     7         return 1
     8     }
     9 
    10     return calcFib(n - 1) + calcFib(n - 2)
    11 }
    12 
    13 func main() {
    14     for i := 0; i <= 10; i++ {
    15         fmt.Println(calcFib(i))
    16     }
    17     
    18 }
    example2

    递归的设计原则:

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

    3)闭包

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

     1 package main
     2 
     3 import "fmt"
     4 
     5 func Adder() func(int) int {
     6     var x int
     7     return func(delta int) int {
     8         x += delta
     9         return x
    10     } 
    11 } 
    12 
    13 func main() {
    14     var f = Adder()       //x的值,只要Adder()还在调用,则x就在内存中
    15     fmt.Println(f(1))     //1
    16     fmt.Println(f(20))    //21 
    17     fmt.Println(f(300))   //321
    18 } 
    闭包例子
     1 package main
     2 
     3 import (
     4     "fmt"
     5     "strings"
     6 )
     7 
     8 //该函数作用检查name是否以suffix结尾,如果不是则追加
     9 func makeSuffixFunc(suffix string) func(string) string {
    10     return func(name string) string {
    11         if !strings.HasSuffix(name, suffix) {
    12             return name + suffix
    13         }
    14         return name
    15     }
    16 }
    17 
    18 func main() {
    19     func1 := makeSuffixFunc(".bmp")
    20     func2 := makeSuffixFunc(".jpg")
    21     fmt.Println(func1("test"))  //test.bmp
    22     fmt.Println(func2("test"))  //test.jpg
    23 }
    闭包例子2

    闭包更多了解详见链接:https://www.cnblogs.com/cxying93/p/6103375.html 和 https://www.cnblogs.com/hzhuxin/p/9199332.html

    2. 数组与切片

    1.数组

         (1). 数组:是同一种数据类型的固定长度的序列。
         (2). 数组定义:var a [len]int,比如:var a[5]int,一旦定义,长度不能变
         (3). 长度是数组类型的一部分,因此,var a[5] int和var a[10]int是不同的类型
         (4). 数组可以通过下标进行访问,下标是从0开始,最后一个元素下标是:len-1

    1 方法1:
    2 for i := 0; i < len(a); i++ {
    3 }
    4 
    5 方法2:
    6 for index, v := range a {
    7 }
    数组访问的两种方式

         (5). 访问越界,如果下标在数组合法范围之外,则触发访问越界,会panic
         (6). 数组是值类型,因此改变副本的值,不会改变本身的值
               arr2 := arr1
               arr2[2] = 100 //arr1的值不会发生改变

     1 package main
     2 
     3 import (
     4     "fmt"
     5 )
     6 
     7 func modify(arr [5]int) { //arr是a的副本
     8     arr[0] = 100
     9     return
    10 }
    11 
    12 func main() {
    13     var a [5]int
    14     
    15     //var a []int
    16     //a = make([]int, 5) // cannot use a (type []int) as type [5]int in argument to modify
    17     
    18     //modify(a[:]) //cannot use a[:] (type []int) as type [5]int in argument to modify
    19     modify(a) //a的值不会发生改变
    20     for i := 0; i < len(a); i++ {
    21         fmt.Println(a[i])
    22     }
    23 }
    不会改变数组的值
     1 package main
     2 
     3 import (
     4     "fmt"
     5 )
     6 
     7 func modify(arr *[5]int) {
     8     //fmt.Printf("%p
    ", arr) //0xc0420481b0
     9     (*arr)[0] = 100
    10     (*arr)[1] = 200
    11     return
    12 }
    13 
    14 func main() {
    15     var a [5]int
    16     //fmt.Printf("%p
    ", &a) //0xc0420481b0
    17     modify(&a) //a的值改变了
    18     for i := 0; i < len(a); i++ {
    19         fmt.Println(a[i])  // a[0]=100  a[1]=200
    20     }
    21 }
    改变数组的值

    注意下面例子:

     1 package main
     2 
     3 import (
     4     "fmt"
     5 )
     6 
     7 func modify(arr [5]int) { //arr是a的副本
     8     arr[0] = 100
     9 }
    10 
    11 func test() {
    12     var b [5]int = [5]int{1, 2, 3, 4, 5}
    13     //fmt.Printf("%p
    ", &b)
    14     modify(b)
    15     fmt.Println(b)  //[1 2 3 4 5] 未改变数组b的值
    16 }
    17 
    18 func modifySlice(a []int) {
    19     fmt.Printf("%p
    ", a) //0xc042042440
    20     a[1] = 1000
    21 }
    22 
    23 func testSlice() {
    24     var b []int = []int{1, 2, 3, 4}
    25     fmt.Printf("%p
    ", b) //0xc042042440
    26     modifySlice(b) //地址的传递
    27     fmt.Println(b)  //[1 1000 3 4] 改变了b的值
    28 }
    29 
    30 func main() {
    31     test()
    32     testSlice()
    33 }
    数组与切片区别

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

     1 package main
     2 
     3 import "fmt"
     4 
     5 func fab(n int) {
     6     var a []uint64
     7     a = make([]uint64, n) //初始化数组 a
     8 
     9     a[0] = 1
    10     a[1] = 1
    11 
    12     for i := 2; i < n; i++ {
    13         a[i] = a[i-1] + a[i-2]
    14     }
    15 
    16     for _, v := range a {
    17         fmt.Println(v)
    18     }
    19 }
    20 
    21 func main() {
    22     fab(10)
    23 }
    斐波拉切

    1). 数组初始化

    1 a. var age0 [5]int = [5]int{1,2,3}
    2 b. var age1 = [5]int{1,2,3,4,5}
    3 c. var age2 = […]int{1,2,3,4,5,6}
    4 d. var str = [5]string{3:”hello world”, 4:”tom”}
    数组初始化

    2). 多维数组

    1 a. var age [5][3]int
    2 b. var f [2][3]int = [...][3]int{{1, 2, 3}, {7, 8, 9}}
    多维数组

    3). 多维数组遍历

     1 package main
     2 
     3 import (
     4     "fmt"
     5 )
     6 
     7 func main() {
     8 
     9     var f[2][3]int = [...][3]int{{1, 2, 3}, {7, 8, 9}}
    10 
    11     for k1, v1 := range f {
    12         for k2, v2 := range v1 {
    13             fmt.Printf("(%d,%d)=%d ", k1, k2, v2)
    14         }
    15         fmt.Println()
    16     }
    17 }
    多维数组遍历

    2. 数组切片

    (1). 切片:切片是数组的一个引用,因此切片是引用类型
    (2).  切片的长度可以改变,因此,切片是一个可变的数组
    (3).  切片遍历方式和数组一样,可以用len()求长度
    (4).  cap可以求出slice最大的容量,0 <= len(slice) <= cap(array),其中array是slice引用的数组
    (5). 切片的定义:var 变量名 []类型,比如 var str []string, var arr []int

    切片引用:
    (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]

    练习:写一个程序,演示切片的各个用法

     1 package main
     2 
     3 import (
     4     "fmt"
     5 )
     6 
     7 func main() {
     8     var slice []int
     9     var arr [5]int = [...]int{1, 2, 3, 4, 5}
    10  
    11     slice = arr[:]
    12     fmt.Println(slice) //[1 2 3 4 5]
    13     slice = slice[1:]
    14     fmt.Println(len(slice)) //4
    15     slice = slice[:len(slice)-1]
    16     fmt.Println(cap(slice)) //4
    17 
    18     slice = slice[0:1]
    19     fmt.Println(len(slice)) //1
    20     fmt.Println(cap(slice)) //4
    21 }
    切片例子

    切片的内存布局:

    练习: 写一个程序,演示切片的内存布局(注意和上图的联系)

     1 package main
     2 
     3 import "fmt"
     4 
     5 //自定义切片类型
     6 type slice struct {
     7     ptr *[10]int //为了测试方便,定义ptr指向具有固定大小10个字节内存地址
     8     len int //数组长度
     9     cap int //数组容量
    10 }
    11 
    12 //初始化切片
    13 func make1(s slice, cap int) slice {
    14     s.ptr = new([10]int)
    15     s.cap = cap
    16     s.len = 0
    17     return s
    18 }
    19 
    20 //修改切片内的数组值
    21 func modify(s slice) {
    22     s.ptr[1] = 1000
    23 }
    24 
    25 func testSlice1() {
    26     var s1 slice
    27     s1 = make1(s1, 10)
    28 
    29     s1.ptr[0] = 100
    30     modify(s1)
    31 
    32     fmt.Println(s1.ptr) //&[100 1000 0 0 0 0 0 0 0 0]
    33 }
    34 
    35 func modify1(a []int) {
    36     a[1] = 1000
    37 }
    38 
    39 func testSlice2() {
    40     var b []int = []int{1, 2, 3, 4}
    41     modify1(b)
    42     fmt.Println(b) //[1 1000 3 4]
    43 }
    44 
    45 func testSlice3() {
    46     var a = [10]int{1, 2, 3, 4}
    47 
    48     b := a[1:5]
    49     fmt.Printf("%p
    ", b) //0xc042058058
    50     fmt.Printf("%p
    ", &a[1]) //0xc042058058
    51 }
    52 
    53 func main() {
    54     //testSlice1()
    55     //testSlice2()
    56     testSlice3()
    57 }
    练习

     (6). 通过make来创建切片

    1 var slice []type = make([]type, len)
    2 slice := make([]type, len)
    3 slice := make([]type, len, cap)
    make创建切片

    (7). 用append内置函数操作切片

    1 slice = append(slice, 10)
    2 var a = []int{1,2,3}
    3 var b = []int{4,5,6}
    4 a = append(a, b…)
    append操作切片

    (8). For range 遍历切片

    1 for index, val := range slice {}
    遍历切片

    (9). 切片resize

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

    (10). 切片拷贝

    1 s1 := []int{1,2,3,4,5}
    2 s2 := make([]int, 10)
    3 copy(s2, s1)
    4 
    5 s3 := []int{1,2,3}
    6 s3 = append(s3, s2…)
    7 s3 = append(s3, 4, 5, 6)
    copy

    (11). string与slice

    1 string底层就是一个byte的数组,因此,也可以进行切片操作
    2 str := "hello world"
    3 s1 := str[0:5]
    4 fmt.Println(s1)
    5 s2 := str[5:]
    6 fmt.Println(s2)
    string与slice

    (12). string的底层布局

    (13). 如何改变string中的字符值?

    1 string本身是不可变的,因此要改变string中字符,需要如下操作:
    2 str := "hello world"
    3 s := []byte(str)
    4 s[0] = 'O'
    5 str = string(s)
    改变string中的值

    (14). 排序和查找操作

    1 排序操作主要都在 sort包中,导入就可以使用了
    2 import("sort")
    3 sort.Ints对整数进行排序,sort.Strings对字符串进行排序, sort.Float64s对
    4 浮点数进行排序.
    5 sort.SearchInts(a []int, b int) 从数组a中查找b,前提是a必须有序
    6 sort.SearchFloats(a []float64, b float64) 从数组a中查找b,前提是a必须有序
    7 sort.SearchStrings(a []string, b string) 从数组a中查找b,前提是a必须有序
    sort
     1 package main
     2 
     3 import (
     4     "fmt"
     5     "sort"
     6 )
     7 
     8 func testIntSort() {
     9     var a = [...]int{1, 8, 38, 2, 348, 484}
    10     sort.Ints(a[:])
    11 
    12     fmt.Println(a) //[1 2 8 38 348 484]
    13 }
    14 
    15 func testStrings() {
    16     var a = [...]string{"abc", "efg", "b", "A", "eeee"}
    17     sort.Strings(a[:])
    18 
    19     fmt.Println(a) //[A abc b eeee efg]
    20 }
    21 
    22 func testFloat() {
    23     var a = [...]float64{2.3, 0.8, 28.2, 392342.2, 0.6}
    24     sort.Float64s(a[:])
    25 
    26     fmt.Println(a) //[0.6 0.8 2.3 28.2 392342.2]
    27 }
    28 
    29 func testIntSearch() {
    30     var a = [...]int{1, 8, 38, 2, 348, 484}
    31     //sort.Ints(a[:]) 
    32     index := sort.SearchInts(a[:], 348) //SearchInts该函数内部会先排序然后查找
    33     fmt.Println(index) //4
    34     fmt.Println(a) //[1 8 38 2 348 484] 未改变a的值
    35 }
    36 
    37 func main() {
    38     testIntSort()
    39     testStrings()
    40     testFloat()
    41     testIntSearch()
    42 }
    example

    注意:sort.Ints,sort.Strings,sort.Float64s会改变源数组的值。

    切片,copy及字符串修改测试:

     1 package main
     2 
     3 import "fmt"
     4 
     5 func testSlice() {
     6     var a [5]int = [...]int{1, 2, 3, 4, 5}
     7     s := a[1:]
     8 
     9     fmt.Printf("before len[%d] cap[%d]
    ", len(s), cap(s)) //before len[4] cap[4]
    10     s[1] = 100 //改变s[1](a[1])值
    11     fmt.Printf("s=%p a[1]=%p
    ", s, &a[1]) //s=0xc0420481b8 a[1]=0xc0420481b8
    12     fmt.Println("before a:", a) //before a: [1 2 100 4 5]
    13 
    14     s = append(s, 10)
    15     s = append(s, 10)
    16     fmt.Printf("after len[%d] cap[%d]
    ", len(s), cap(s)) //after len[6] cap[8]
    17     s = append(s, 10)
    18     s = append(s, 10)
    19     s = append(s, 10)
    20 
    21     s[1] = 1000 //s扩容之后s和a[1]的地址不同,因此改变s[1]的值a[1]的值不发生变化
    22     fmt.Println("after a:", a) //after a: [1 2 100 4 5]
    23     fmt.Println(s) //[2 1000 4 5 10 10 10 10 10]
    24     fmt.Printf("s=%p a[1]=%p
    ", s, &a[1])  //s=0xc042014200 a[1]=0xc0420481b8
    25 }
    26 
    27 func testCopy() {
    28     var a []int = []int{1, 2, 3, 4, 5, 6}
    29     b := make([]int, 1) 
    30 
    31     copy(b, a) //拷贝的大小以b为准
    32 
    33     fmt.Println(b) //[1]
    34 }
    35 
    36 func testString() {
    37     s := "hello world"
    38     s1 := s[0:5]
    39     s2 := s[6:]
    40 
    41     fmt.Println(s1)  //hello
    42     fmt.Println(s2)  //world
    43 }
    44 
    45 func testModifyString() {
    46     s := "我hello world"
    47     s1 := []rune(s)
    48 
    49     s1[0] = ''
    50     s1[1] = 'z'
    51     s1[2] = 'z'
    52     str := string(s1)
    53     fmt.Println(str)  //你zzllo world
    54 }
    55 
    56 func main() {
    57     //testSlice()
    58     //testCopy()
    59     //testString()
    60     testModifyString()
    61 }
    example

    3. map数据结构

    (1). map简介
          key-value的数据结构,又叫字典或关联数组
          声明:

          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

    (2). map相关操作
         var a map[string]string = map[string]string{"hello": "world"}  定义并初始化map a
         a := make(map[string]string, 10)
         a["hello"] = "world" 插入和更新
         val, ok := a["hello"] 查找
         for k, v := range a {
              fmt.Println(k,v) 遍历
         }

         delete(a, "hello") 删除
         len(a) 长度

    (3). map是引用类型
         func modify(a map[string]int) {
              a["one"] = 134
         }

    (4). slice of map
         items := make([]map[int][int], 5)
         for i := 0; I < 5; i++ {
               items[i] = make(map[int][int])
         }

    (5). map排序
        a. 先获取所有key,把key进行排序
        b. 按照排序好的key,进行遍历

    (6). Map反转
        初始化另外一个map,把key、value互换即可

     1 package main
     2 
     3 import "fmt"
     4 
     5 func testMap() {
     6     var a map[string]string = map[string]string {
     7         "key": "value",
     8     }
     9     //a := make(map[string]string, 10)
    10     a["abc"] = "efg"
    11     a["abc"] = "efg"
    12     a["abc1"] = "efg"
    13 
    14     fmt.Println(a)  //map[key:value abc:efg abc1:efg]
    15 }
    16 
    17 func testMap2() {
    18 
    19     a := make(map[string]map[string]string, 100) //value是一个map
    20     a["key1"] = make(map[string]string)
    21     a["key1"]["key2"] = "abc"
    22     a["key1"]["key3"] = "abc"
    23     a["key1"]["key4"] = "abc"
    24     a["key1"]["key5"] = "abc"
    25     fmt.Println(a)  //map[key1:map[key2:abc key3:abc key4:abc key5:abc]]
    26 }
    27 
    28 func modify(a map[string]map[string]string) {
    29     _, ok := a["zhangsan"]
    30     if !ok {
    31         a["zhangsan"] = make(map[string]string)
    32     }
    33 
    34     a["zhangsan"]["passwd"] = "123456"
    35     a["zhangsan"]["nickname"] = "pangpang"
    36 
    37     return
    38 }
    39 
    40 func testMap3() {
    41 
    42     a := make(map[string]map[string]string, 100)
    43 
    44     modify(a)
    45 
    46     fmt.Println(a) //map[zhangsan:map[passwd:123456 nickname:pangpang]]
    47 }
    48 
    49 func trans(a map[string]map[string]string) {
    50     for k, v := range a {
    51         fmt.Println(k)
    52         for k1, v1 := range v {
    53             fmt.Println("	", k1, v1)
    54         }
    55     }
    56 }
    57 
    58 func testMap4() {
    59 
    60     a := make(map[string]map[string]string, 100)
    61     a["key1"] = make(map[string]string)
    62     a["key1"]["key2"] = "abc"
    63     a["key1"]["key3"] = "abc"
    64     a["key1"]["key4"] = "abc"
    65     a["key1"]["key5"] = "abc"
    66 
    67     a["key2"] = make(map[string]string)
    68     a["key2"]["key2"] = "abc"
    69     a["key2"]["key3"] = "abc"
    70 
    71     trans(a)
    72     delete(a, "key1") //删除"key1"
    73     fmt.Println()
    74     trans(a)
    75 
    76     fmt.Println(len(a))
    77 }
    78 
    79 func testMap5() {
    80     var a []map[int]int
    81     a = make([]map[int]int, 5)
    82 
    83     if a[0] == nil {
    84         a[0] = make(map[int]int)
    85     }
    86     a[0][10] = 10
    87     fmt.Println(a)  //[map[10:10] map[] map[] map[] map[]]
    88 }
    89 
    90 func main() {
    91     //testMap()
    92     //testMap2()
    93     //testMap3()
    94     //testMap4()
    95     testMap5()
    96 }
    Map操作示例
     1 package main
     2 
     3 import (
     4     "fmt"
     5     "sort"
     6 )
     7 
     8 func testMapSort() {
     9     var a map[int]int
    10     a = make(map[int]int, 5)
    11 
    12     a[8] = 10
    13     a[3] = 10
    14     a[2] = 10
    15     a[1] = 10
    16     a[18] = 10
    17 
    18     var keys []int
    19     for k, _ := range a {
    20         keys = append(keys, k)
    21         //fmt.Println(k, v)
    22     }
    23 
    24     sort.Ints(keys)
    25 
    26     for _, v := range keys {
    27         fmt.Println(v, a[v])
    28     }
    29  //     1 10
    30  //     2 10
    31  //     3 10
    32  //     8 10
    33  //    18 10
    34 }
    35 
    36 func testMapSort1() {
    37     var a map[string]int
    38     var b map[int]string
    39 
    40     a = make(map[string]int, 5)
    41     b = make(map[int]string, 5)
    42 
    43     a["abc"] = 101
    44     a["efg"] = 10
    45 
    46     for k, v := range a {
    47         b[v] = k
    48     }
    49 
    50     fmt.Println(b)
    51 }
    52 
    53 func main() {
    54     //testMapSort()
    55     testMapSort1()
    56 }
    Map sort示例

    4. 包

    (1). golang中的包
          a. golang目前有150个标准的包,覆盖了几乎所有的基础库
          b. golang.org有所有包的文档,没事都翻翻

    (2). 线程同步
          a. import("sync")
          b. 互斥锁, var mu sync.Mutex
          c. 读写锁, var mu sync.RWMutex

    1)锁的概念:
         什么是锁呢?就是某个协程(线程)在访问某个资源时先锁住,防止其它协程的访问,等访问完毕解锁后其他协程再来加锁进行访问。

    2)互斥锁:

         每个资源都对应于一个可称为 “互斥锁” 的标记,这个标记用来保证在任意时刻,只能有一个协程(线程)访问该资源。其它的协程只能等待。

    注意:在使用互斥锁时,一定要注意:对资源操作完成后,一定要解锁,否则会出现流程执行异常,死锁等问题。通常借助defer。锁定后,立即使用defer语句保证互斥锁及时解锁。如下所示:

    1 var mutex sync.Mutex // 定义互斥锁变量 mutex
    2 
    3 func write() { 
    4      mutex.Lock( ) 
    5      defer mutex.Unlock( ) 
    6 }
    互斥锁释放

    3)读写锁:

          互斥锁的问题:互斥锁的本质是当一个goroutine访问的时候,其他goroutine都不能访问。这样在资源同步,避免竞争的同时也降低了程序的并发性能。程序由原来的并行执行变成了串行执行。真正的互斥应该是读取和修改、修改和修改之间,读和读是没有互斥操作的必要的。因此,衍生出另外一种锁,叫做读写锁。因此,读写锁是针对于读写操作的互斥锁。

    基本遵循两大原则:
        1、可以随便读。多个goroutin同时读。
        2、写的时候,啥都不能干。不能读,也不能写。

     1 package main
     2 
     3 import (
     4     "fmt"
     5     "math/rand"
     6     "sync"
     7     "sync/atomic"
     8     "time"
     9 )
    10 
    11 var lock sync.Mutex
    12 var rwLock sync.RWMutex
    13 
    14 func testMap() {
    15     var a map[int]int
    16     a = make(map[int]int, 5)
    17 
    18     a[8] = 10
    19     a[3] = 10
    20     a[2] = 10
    21     a[1] = 10
    22     a[18] = 10
    23 
    24     for i := 0; i < 2; i++ {
    25         go func(b map[int]int) {
    26             lock.Lock()
    27             b[8] = rand.Intn(100)
    28             lock.Unlock()
    29         }(a)
    30     }
    31 
    32     lock.Lock()
    33     fmt.Println(a)
    34     lock.Unlock()
    35 
    36     time.Sleep(time.Second)
    37 }
    38 
    39 func testRWLock() {
    40     var a map[int]int
    41     a = make(map[int]int, 5)
    42     var count int32
    43     a[8] = 10
    44     a[3] = 10
    45     a[2] = 10
    46     a[1] = 10
    47     a[18] = 10
    48 
    49     for i := 0; i < 2; i++ {
    50         go func(b map[int]int) {
    51             //rwLock.Lock()
    52             lock.Lock()
    53             b[8] = rand.Intn(100)
    54             time.Sleep(10 * time.Millisecond)
    55             lock.Unlock()
    56             //rwLock.Unlock()
    57         }(a)
    58     }
    59 
    60     for i := 0; i < 100; i++ {
    61         go func(b map[int]int) {
    62             for {
    63                 lock.Lock()
    64                 //rwLock.RLock()
    65                 time.Sleep(time.Millisecond)
    66                 //fmt.Println(a)
    67                 //rwLock.RUnlock()
    68                 lock.Unlock()
    69                 atomic.AddInt32(&count, 1)
    70             }
    71         }(a)
    72     }
    73     time.Sleep(time.Second * 3)
    74     fmt.Println(atomic.LoadInt32(&count))
    75 }
    76 
    77 func main() {
    78     //testMap()
    79     testRWLock()
    80 }
    互斥锁及读写锁示例

    (3). go get安装第三方包

        go get 可以借助代码管理工具通过远程拉取或更新代码包及其依赖包,并自动完成编译和安装。整个过程就像安装一个 App 一样简单。
        使用 go get 前,需要安装与远程包匹配的代码管理工具,如 Git、SVN、HG 等,参数中需要提供一个包名。

    go get+远程包
         默认情况下,go get 可以直接使用。例如,想获取 go 的源码并编译,使用下面的命令行即可:
        $ go get github.com/davyxu/cellnet

        注意:获取前,请确保 GOPATH 已经设置。Go 1.8 版本之后,GOPATH 默认在用户目录的 go 文件夹下。

    5. 排序相关

        1). 实现一个冒泡排序

     1 package main
     2 
     3 import "fmt"
     4 
     5 func bsort(a []int) {
     6 
     7     for i := 0; i < len(a); i++ {
     8         for j := 1; j < len(a)-i; j++ {
     9             if a[j] < a[j-1] {
    10                 a[j], a[j-1] = a[j-1], a[j]
    11             }
    12         }
    13     }
    14 }
    15 
    16 func main() {
    17     b := [...]int{8, 7, 5, 4, 3, 10, 15}
    18     bsort(b[:])
    19     fmt.Println(b)
    20 }
    bsort

        2). 实现一个选择排序

     1 package main
     2 
     3 import "fmt"
     4 
     5 func ssort(a []int) {
     6 
     7     for i := 0; i < len(a); i++ {
     8         var min int = i
     9         for j := i + 1; j < len(a); j++ {
    10             if a[min] > a[j] {
    11                 min = j
    12             }
    13         }
    14         a[i], a[min] = a[min], a[i]
    15     }
    16 }
    17 
    18 func main() {
    19     b := [...]int{8, 7, 5, 4, 3, 10, 15}
    20     ssort(b[:])
    21     fmt.Println(b)
    22 }
    ssort

        3). 实现一个插入排序

     1 package main
     2 
     3 import "fmt"
     4 
     5 func isort(a []int) {
     6 
     7     for i := 1; i < len(a); i++ {
     8         for j := i; j > 0; j-- {
     9             if a[j] > a[j-1] {
    10                 break
    11             }
    12             a[j], a[j-1] = a[j-1], a[j]
    13         }
    14     }
    15 }
    16 
    17 func main() {
    18     b := [...]int{8, 7, 5, 4, 3, 10, 15}
    19     isort(b[:])
    20     fmt.Println(b)
    21 }
    isort

        4). 实现一个快速排序

     1 package main
     2 
     3 import "fmt"
     4 
     5 func qsort(a []int, left, right int) {
     6     if left >= right {
     7         return
     8     }
     9 
    10     val := a[left]
    11     k := left
    12     //确定val所在的位置
    13     for i := left + 1; i <= right; i++ {
    14         if a[i] < val {
    15             a[k] = a[i]
    16             a[i] = a[k+1]
    17             k++
    18         }
    19     }
    20 
    21     a[k] = val
    22     qsort(a, left, k-1)
    23     qsort(a, k+1, right)
    24 }
    25 
    26 func main() {
    27     b := [...]int{8, 7, 5, 4, 3, 10, 15}
    28     qsort(b[:], 0, len(b)-1)
    29     fmt.Println(b)
    30 }
    qsort

     参考文献:

    • https://www.jianshu.com/p/0cbc97bd33fb(Go语言 异常panic和恢复recover用法)
    • https://blog.csdn.net/weixin_42927934/article/details/82533940(读写锁、互斥锁)
    • http://c.biancheng.net/view/123.html (go get命令——一键获取代码、编译并安装)
  • 相关阅读:
    Qt 串口通信 高速发送出错的解决方法总结
    Qt VS MFC
    从char到QChar
    QObject 的拷贝构造和赋值操作
    qt 元对象系统
    QT Embedded二三事之QObject的元对象
    QTableWidget与QTableView的区别
    arcengine,深入理解游标Cursors,实现数据的快速查找,插入,删除,更新
    利用IIdentify接口实现点选和矩形选择要素
    IWorkSpace与IWorkSpaceFactory与IWorkSpaceEdit
  • 原文地址:https://www.cnblogs.com/xuejiale/p/10371828.html
Copyright © 2020-2023  润新知