• Go-day04


    今日概要:

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

      2.数组与切片

      3.map数据结构

      4.package介绍

      5.互斥锁和读写锁

    一、内置函数

      1.close:主要用来关闭channel

      2.len:用来求长度,比如string、array、slice、map、channel

      3.new:用来分配内存,主要用来分配值类型,比如int,struct返回的是指针

      4.make:用来分配内存,主要用来分配引用类型,比如chan,map,slice

      5.append:用来追加元素到数组、slice

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

    new的例子:

    package main
    
    import "fmt"
    
    func main() {
    	var i int
    	fmt.Println(i)
    
    	j := new(int) //值类型分配内存地址
    	fmt.Println(j) //打印的是内存地址
    	*j = 100
    	fmt.Println(*j) //打印指针对应的值
    }
    
    
    /*
    0
    0xc420014060
    100
    */

    new和make的区别:

          

    package main
    
    import "fmt"
    
    func mytest(){
    
    	a := new([]int) //创建一个容量为0的切片,a为内存地址,当前为nil不能放值
    
    	b := make([]int,10)
    
    	fmt.Println(a)
    	fmt.Println(b)
    
    
    	//将切片插入值,a为内存地址只能操作指针
    	*a = make([]int,5)  //通过make初始化容量为5
    	(*a)[0] = 100 //panic: runtime error: index out of range 因为空切片需要初始化
    	b[0] = 100
    	fmt.Println(a)
    	fmt.Println(b)
    }
    
    func main() {
    	mytest()
    }
    /*
    &[]
    [0 0 0 0 0 0 0 0 0 0]
    &[100 0 0 0 0]
    [100 0 0 0 0 0 0 0 0 0]
    */
    

     内置变量append

      数组里append数组可以用数组...添加

      目前有用到...的有 函数的可变长参数...,append加数组... 数组设置不想设置容量的时候 var a [...]int

    package main
    
    import "fmt"
    
    func main() {
    	var a []int
    	a = append(a,20,3,40)
    	a = append(a,a...) //数组和数组相加可以用数组...
    	fmt.Println(a)
    
    }
    

    内置变量panic和recover

      recover是捕获异常,可以让服务不停止,比如一些异常想报警,但是又保证服务正常运行可以用defer+recover

    package main
    
    import (
    	"time"
    	_ "fmt"
    	"errors"
    )
    
    func getConfig() (err error){
    	return errors.New("init config failed!") //创建异常
    }
    
    func test(){
    	/*
    	//通过recover捕获异常程序不退出
    	defer func(){
    		if err := recover(); err != nil{
    			fmt.Println(err)
    		}
    	}()
    
    	b := 0
    	a := 100 / b //异常
    	fmt.Println(a)
    	*/
    	err := getConfig()
    	if err != nil{
    		panic(err) //通过panic抛出异常与python中的raise一样
    	}
    	return
    
    }
    
    func main() {
    	for {
    		test()
    		time.Sleep(time.Second * 1)
    	}
    
    }
    

     二、递归函数

      go里面的递归函数和python的很像,python支持递归1000层,go支持的远远大于python

    设计原则

      1.一个大问题能够分解成相似的小问题

      2.定义好出口条件

    通过递归求阶乘

    package main
    
    import "fmt"
    
    //通过递归求阶乘
    func calc(n int) int{
    	if n == 1{
    		return 1
    	}
    	return calc(n-1) *n
    }
    
    func main() {
    	m := calc(5)
    	fmt.Println(m)
    }
    
    /*
    120
    */
    

     通过递归求斐波那契

    package main
    
    import "fmt"
    
    func fab(n int) int{
    	if n <= 1{
    		return 1
    	}
    	return fab(n-1) + fab(n-2)
    
    }
    
    func main() {
    	for i :=0 ; i < 10 ; i++ {
    		fmt.Println(fab(i))
    	}
    
    
    }
    /*
    1
    1
    2
    3
    5
    8
    13
    21
    34
    55
    */
    

     三、闭包

      一个函数和与其相关的引用环境组合而成的实体,相当于闭包里面的变量,所有闭包都能读取

    闭包例子一:

    package main
    
    import "fmt"
    
    //返回值是一个func函数
    func Adder() func (int) int { //类似一个类,x为类变量,修改都生效
    	var x int
    	return func(d int) int{//定义了一个匿名函数
    		x += d
    		return x
    	}
    }
    
    func main() {
    	f := Adder()
    	fmt.Println(f(1))
    	fmt.Println(f(20))
    	fmt.Println(f(100))
    
    }
    
    /*
    1
    21
    121
    */
    

     闭包例子二:

      定义的时候传入了参数,类似于python里的实例化

    package main
    
    import (
    	"strings"
    	"fmt"
    )
    
    type str func(string) string
    
    func makeSuffix(suffix string)  str { //make的时候传入suffix
    
    	return func(name string) string{
    		if strings.HasSuffix(name,suffix) == false{
    			return name + suffix
    		}
    		return name
    	}
    
    }
    
    
    func main() {
    	f1 := makeSuffix(".mp3")
    
    	fmt.Println(f1("bgm"))
    
    	f2 := makeSuffix(".mp4")
    
    	fmt.Println(f2("av"))
    
    
    }
    
    /*
    bgm.mp3
    av.mp4
    */
    

     四、数组与切片

      数组:

        1.数组:是同一种数据类型的固定长度的序列。

        2.数组定义:var a [len]int,比如:var a [5]int 一旦定义,长度不能改变

        3.长度是数组类型的一部分,因此,var a [5] int和var a [10]int是不同的类型

        4.数组可以通过下标进行访问,下标是从0开始,最后一个元素下标是:len-1

        5.访问越界,如果下标在数组合法范围之外,则触发访问越界,会panic

        6.数组是值类型,因此改变副本的值,不会改变本身的值

    定义6的验证:

    package main
    
    import "fmt"
    
    func test2(){
    
    	var a [10]int //数组是值变量,赋值之后新的改变,老的不会改变
    	b := a
    	b[0] = 100
    	fmt.Println(a) //[0 0 0 0 0]
    }
    
    func main() {
    	test1()
    
    }
    
    /*
    [0 0 0 0 0]
    */
    

    变成指针修改当前数据原数据也被修改

    package main
    
    import "fmt"
    
    func test3(a *[5]int){ //变成指针就成为引用类型
    	(*a)[0] = 10
    }
    
    func main() {
    
    	var a [5]int
    	test3(&a)
    	fmt.Println(a)
    
    
    }
    
    /*
    [10 0 0 0 0]
    */
    

     练习:

      使用非递归的方式实现斐波那契数列,打印前100个数

    package main
    
    import "fmt"
    
    func fab1(n int){
    	var sli []int64  //声明了一个切片
    	sli = make([]int64,n)//将切片设定容量
    	sli[0] = 1
    	sli[1] = 1
    	for i :=2 ; i < len(sli);i++{
    		sli[i] = sli[i-1] + sli[i-2]
    	}
    
    	for _,value := range sli{ //通过for,range遍历切片
    		fmt.Println(value)
    	}
    }
    
    func main() {
    	fab1(10)
    }
    /*
    1
    1
    2
    3
    5
    8
    13
    21
    34
    55
    */
    

    循环数组的两种方式:

    package main
    
    import "fmt"
    
    func test1(){
    	var a [5]int //定义一个数组
    
    	fmt.Println(a)
    
    	//循环数组两种方式
    	//方式一
    	for i := 0; i < len(a) ; i++{
    		fmt.Println(a[i])
    	}
    	//方式二
    	for key,value := range a{
    		fmt.Printf("%d=[%d]
    ",key,value)
    	}
    
    
    }
    
    
    func main() {
    	test1()
    }

     数组的初始化:

      a. var age0 [5]int = [5]int{1,2,3}

      b. var age1 = [5]int{1,2,3,4,5}

      c. var age2 = […]int{1,2,3,4,5,6} 编译的时候自动求出来长度

      d. var str = [5]string{3:”hello world”, 4:”tom”}

    多维数组和多维数组遍历  

      a. var age [5][3]int

      b. var f [2][3]int = [...][3]int{{1, 2, 3}, {7, 8, 9}} 定义了一个二维数组

    例子:

    package main
    
    import "fmt"
    
    //数组的初始化
    func testArray(){
    	var a [5]int = [5]int{1,2,3,4,5}
    	var a1 = [5]int{2,3,4,5,6}
    	var a2 =  [...]int{1,2,3}
    	var a3 = [...]string{1:"hello",3:"world"}
    	fmt.Println(a)
    	fmt.Println(a1)
    	fmt.Println(a2)
    	fmt.Println(a3)
    }
    
    //多维数组
    
    func testArray1() {
    	var a = [2][5]int{{1,2,3,4,5},{6,7,8,9,0}} //第一个是有几个{},第二个是每个{}有几个元素
    	fmt.Println(a)
    	var b = [...][5]int{{1,2,3,4,5},{6,7,8,9,0}} //一维可以加...,其他不行
    	fmt.Println(b)
    
    	//多维数组遍历
    	for row,v := range a{
    		for col,v1 := range v{
    			fmt.Printf("row:%d,col:%d,val:%d	",row,col,v1)
    		}
    		fmt.Println()
    	}
    }
    
    
    func main() {
    	testArray()
    	testArray1()
    }
    
    /*
    [1 2 3 4 5]
    [2 3 4 5 6]
    [1 2 3]
    [ hello  world]
    */
    

     切片:

      1. 切片:切片是数组的一个引用,因此切片是引用类型

      2. 切片的长度可以改变,因此,切片是一个可变的数组

      3. 切片遍历方式和数组一样,可以用len()求长度

      4.cap可以求出slice最大的容量,0 <= len(slice) <= (array),其中array是slice引用的数组

      5. 切片的定义:var 变量名 []类型,比如 var str []string  var arr []int

      6. 切片初始化:var slice []int = arr[start:end]  包含start到end之间的元素,但不包含end

      7. var slice []int = arr[0:end]可以简写为 var slice []int=arr[:end]

      8. var slice []int = arr[start:len(arr)] 可以简写为 var slice[]int = arr[start:]

      9. var slice []int = arr[0, len(arr)] 可以简写为 var slice[]int = arr[:]

      10. 如果要切片最后一个元素去掉,可以这么写: slice = slice[:len(slice)-1]

      关于容量详解:

    Array_a := [10]byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}
    Slice_a := Array_a[2:5]
    

      上面的代码真实存储方式,cap的容量是截取空间到最后的大小

      

    练习:

    package main
    
    import "fmt"
    
    func testSlice1() {
    	//切片是引用类型,传入函数中会改变原有值
    	var slice []int
    	var arr [5]int = [5]int{1, 2, 3, 4, 5}
    
    	slice = arr[:]
    	slice = slice[1:]
    	slice = slice[:len(slice)-1] //刨除最后一个
    	fmt.Println(slice)
    	fmt.Println(len(slice))
    	fmt.Println(cap(slice))
    
    	slice = slice[0:1] //切片可以在切片,这样cap容量不变,len会变小
    	fmt.Println(len(slice))
    	fmt.Println(cap(slice))
    }
    
    func main() {
    
    	testSlice1()
    }
    
    /*
    [2 3 4]
    3
    4
    1
    4
    */
    

     切片的内存布局:

        切片本质上最底层还是数组,切片只是指针指向切片第一个值的内存地址,如果切片append的容量大于数组容量,底层会重新创建一个数组cap的值是成倍增长的

        append函数会改变slice所引用的数组的内容,从而影响到引用同一数组的其它slice。 但当slice中没有剩余空间(即(cap-len) == 0)时,此时将动态分配新的数组空间。返回的slice数组指针将指向这个空间,而原数组的内容将保持不变;其它引用此数组的slice则不受影响。

        

    验证切片的内存地址和原数组切片第一个值的内存地址一致:

    package main
    
    import "fmt"
    
    func testArray2(){
    	var a = [10]int{1,2,3,4}
    
    	b := a[1:3]
    	fmt.Printf("%p
    ",b)  //验证b切片和数组内存地址一样,证明切片就是指针
    
    	fmt.Printf("%p
    ",&a[1])//因为从第一个元素切的所以指针指向第一个内存地址
    
    }
    
    func main() {
    	testArray2()
    }
    
    /*
    0xc42001a0f8
    0xc42001a0f8
    */
    

    通过自定义切片slice模拟切片操作

    package main
    
    import "fmt"
    
    type slice struct { //封装成一个结构体
    	ptr *[100]int
    	len int
    	cap int
    }
    
    func make1(s slice,cap int) slice{
    	s.ptr = new([100]int)
    	s.cap = cap
    	s.len = 0
    	return s
    }
    
    func modify(s slice){
    	s.ptr[1] = 1000
    }
    
    func testSlice(){
    	var s1 slice
    	s1 = make1(s1,10)
    	s1.ptr[0] = 100
    
    	modify(s1)
    
    	fmt.Println(s1.ptr)
    
    }
    
    
    func main() {
    
    	testSlice()
    
    }
    /*
    &[100 1000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
    */
    

    通过make来创建切片  

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

      slice  := make([]type, len)

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

    底层实现(生成了一个cap为5的数组,将切片的指针指向数组的第一个值):

        

    用append内置函数操作切片:

      slice = append(slice, 10)

      var a = []int{1,2,3}

      var b = []int{4,5,6}

      a = append(a, b…) 切片插入切片的时候用切片名加...

    练习:

    package main
    
    import "fmt"
    
    func testArray3(){
    	var arr [5]int = [...]int{1,2,3,4,5}
    
    
    	s := arr[1:3]
    	fmt.Printf("before:len[%d],cap[%d]
    ",len(s),cap(s))
    
    	s[1] = 100
    	fmt.Printf("arr=%p,slice=%p
    ",&arr[1],s)
    	fmt.Println("before append",arr)
    	s = append(s,10)
    	s = append(s,10)
    
    	s = append(s,10)
    	fmt.Printf("after:len[%d],cap[%d]
    ",len(s),cap(s)) //容量翻倍增加
    	s = append(s,10)
    	s = append(s,10)
    
    	s[1] = 1000
    	fmt.Println(s)
    	fmt.Println("after append",arr) //a的值还会变,只不过没超出之前改的还是原来的内存地址
    	fmt.Printf("arr=%p,slice=%p
    ",&arr[1],s) //切片是可变长的,底层会创建一个新的数组,将指针指向新的数组
    
    }
    
    func main() {
    	testArray3()
    }
    
    /*
    before:len[2],cap[4]
    arr=0xc42001c098,slice=0xc42001c098
    before append [1 2 100 4 5]
    after:len[5],cap[8]
    [2 1000 10 10 10 10 10]
    after append [1 2 100 10 10]
    arr=0xc42001c098,slice=0xc420018080
    */
    

     for range切片遍历:

      for index, val := range slice {
      }

    切片resize

      var a = []int {1,3,4,5}

      b := a[1:2]

      b = b[0:3]

    切片copy

    package main
    
    import "fmt"
    
    func testCopy(){
    
    	var a []int = []int{1,2,3,4,5}
    
    	b := make([]int,10) //[0,0,0,0,0,0,0,0,0,0]
    
    	copy(b,a) //内置方法copy
    
    	fmt.Println(b)
    }
    
    func main() {
    	testCopy()
    
    }
    
    /*
    [1 2 3 4 5 0 0 0 0 0]
    */
    

     string和slice

      string底层就是一个byte的数组,因此,也可以进行切片操作

    string的底层布局 

      

    如何改变string中的字符值?  

      string本身是不可变的,因此要改变string中字符,需要如下操作:

    package main
    
    import "fmt"
    
    func testModifyString(){
    	var a string = "我hello world"
    	b := []rune(a) //有中文的时候需要通过字符数组更改,强制转为数组
    	b[0] = '1'
    	fmt.Println(string(b))
    
    
    }
    func main() {
    	testModifyString()
    }
    

     slice的排序和查找操作:

      排序操作主要都在 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 (
    	"sort"
    	"fmt"
    )
    
    func testIntSort(){
    	var a = [...]int{22,33,1,3,4} //数组是不能排序的,因为他是值类型
    
    
    	sort.Ints(a[:]) //将传入的值变为切片
    	fmt.Println(a)
    
    }
    
    func testStringSort(){
    	var a = [...]string{"aa","ccc","bbb","ffff"}
    
    	sort.Strings(a[:])
    	fmt.Println(a)
    
    }
    func testFloatSort(){
    	var a = [...]float64{0.38,0.02,0.01,0.44}
    
    	sort.Float64s(a[:])
    	fmt.Println(a)
    
    }
    
    func testIntSearch(){
    	var a = [...]int{22,33,1,3,4}
    
    	sort.Ints(a[:])
    	index := sort.SearchInts(a[:],22) //即便你不排序,默认也会排序,但是值会混乱
    	fmt.Println(index)
    }
    
    
    func main() {
    	testIntSort()
    	testStringSort()
    	testFloatSort()
    	testIntSearch()
    
    
    }
    
    /*
    [1 3 4 22 33]
    [aa bbb ccc ffff]
    [0.01 0.02 0.38 0.44]
    3
    
    */
    

      注:从Go1.2开始slice支持了三个参数的slice,之前我们一直采用这种方式在slice或者array基础上来获取一个slice 

        var array [10]int
        slice := array[2:4]

        这个例子里面slice的容量是8,新版本里面可以指定这个容量

        slice = array[2:4:7] 第三位是容量

        上面这个的容量就是7-2,即5。这样这个产生的新的slice就没办法访问最后的三个元素。

        如果slice是这样的形式array[:i:j],即第一个参数为空,默认值就是0。

    五、map(类似python的dict) 

      key-value的数据结构,又叫字典或关联数组

      a.声明

        声明是不会分配内存的,初始化需要make

        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

      b.map的相关操作

        var a map[string]string = map[string]string{“hello”: “world”} 声明加赋值

        a = make(map[string]string, 10) 初始化map

        a[“hello”] = “world” 插入

        val, ok := a[“hello”] 查找2个返回值,第一个是值,第二个为是否查到

        for k, v := range a {  遍历map

          fmt.Println(k,v)

        }

        delete(a, “hello”) 删除map中的key

        len(a) 求map长度

    例子:声明初始化和make初始化

    package main
    
    import "fmt"
    
    func testMap(){
    	//声明初始化
    	var a map[string]string = map[string]string{
    		"asdas":"asdsdasd",
    	}
    	//make初始化
    	//a := make(map[string]string,10)
    
    	a["abc"] = "cde"
    
    	fmt.Println(a)
    
    }
    func main() {
    	testMap()
    
    }
    

    map是引用类型:  

        func modify(a map[string]int) {

          a[“one”] = 134 //原来的map也会改变

        }

    slice里面放map

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

    练习:

    package main
    
    import "fmt"
    
    func testMap(){
    	//声明初始化
    	var a map[string]string = map[string]string{
    		"asdas":"asdsdasd",
    	}
    	//make初始化
    	//a := make(map[string]string,10)
    
    	a["abc"] = "cde"
    
    	fmt.Println(a)
    
    }
    
    //多维数组
    func testMap2(){
    	//因为有2层数组,需要初始化两次,才能使用,map,chan,slice都需要初始化,声明为nil
    	a := make(map[string]map[string]string,100)
    	a["key1"] = make(map[string]string,10)
    	a["key1"]["key2"] = "val1"
    	a["key1"]["key3"] = "val2"
    	a["key1"]["key4"] = "val3"
    	a["key1"]["key5"] = "val4"
    
    	fmt.Println(a)
    
    }
    
    func modify2 (a map[string]map[string]string){
    
    	//查找
    	_, ok := a["zhangsan"] //固定写法,第一个为值,第二个为bool
    	if !ok {
    		a["zhangsan"] = make(map[string]string) //判断zhangsan是否存在,不存在就创建zhangsan与对应都map
    	}
    
    	/* 等价写法
    	if a["zhangsan"] == nil{
    		a["zhangsan"] = make(map[string]string)
    	}
    	*/
    	a["zhangsan"]["nickname"] = "pangpang"
    	a["zhangsan"]["age"] = "18"
    
    	return
    
    }
    
    //多维数组做用户信息保存
    func testMap3(){
    	a := make(map[string]map[string]string,100)
    	modify2(a)
    	fmt.Println(a)
    }
    
    func trans(a map[string]map[string]string){
    	//遍历map
    	for k,v := range a{
    		fmt.Println(k)
    		for k1,v1 := range v{
    			fmt.Println("	",k1,v1)
    		}
    	}
    }
    
    //多维数组一些操作
    func testMap4(){
    	//遍历map
    	a := make(map[string]map[string]string,100)
    	a["key1"] = make(map[string]string,10)
    	a["key1"]["key2"] = "val1"
    	a["key1"]["key3"] = "val2"
    	a["key1"]["key4"] = "val3"
    	a["key1"]["key5"] = "val4"
    
    
    	a["key2"] = make(map[string]string,10)
    	a["key2"]["key2"] = "val1"
    	a["key2"]["key3"] = "val2"
    	trans(a)
    	//删除map中key,map要全部清空for循环,或者重新make下
    	delete(a,"key1")
    	trans(a)
    	//求长度
    	fmt.Println(len(a))
    
    
    
    }
    
    func testMap5(){
    	var a []map[int]int
    	a = make([]map[int]int,5)
    
    	if a[0] == nil{  //默认只声明的map,或者slice都是nil
    		a[0] = make(map[int]int)
    	}
    
    	a[0][10] = 100
    	fmt.Println(a)
    }
    
    func main() {
    	//testMap()
    	//testMap2()
    	//testMap3()
    	//testMap4()
    	testMap5()
    }
    

     map的排序

      a. 先获取所有key,把key进行排序

      b. 按照排序好的key,进行遍历

    package main
    
    import (
    	"fmt"
    	"sort"
    )
    
    func testMapSort(){
    	//保证排序正常
    	var a map[int]int
    	a = make(map[int]int,5)
    
    	a[0] = 1
    	a[2] = 3
    	a[3] = 4
    	a[10] =100
    
    	fmt.Println(a)
    
    	var keys []int
    	for k, _ := range a{
    		keys = append(keys,k) //append会直接增加不需要make
    	}
    	sort.Ints(keys)
    	for _,v := range keys{
    		fmt.Println(a[v])
    	}
    
    }
    
    func main() {
    	testMapSort()
    }
    

     map的反转

      a. 初始化一个新map,把key、value互换即可

    package main
    
    import (
    	"fmt"
    )
    
    func testMapReverse(){
    	//反转要新生成一个map,需要注意字符类型
    	var a map[string]int
    	a = make(map[string]int,5)
    
    	var b map[int]string
    	b = make(map[int]string,5)
    
    	a["aaa"] = 1
    	a["bbb"] = 3
    
    	for k,v := range a{
    		b[v] = k
    	}
    	fmt.Println(b)
    
    }
    
    func main() {
    	testMapReverse()
    }
    

    六、go中的sync包  

      a. import(“sync”)

      b. 互斥锁, var lock sync.Mutex

      c. 读写锁, var rwlock sync.RWMutex  

      读写锁适用范围:读多写少的场景,读写锁是互斥锁性能的100多倍

      在读写锁机制下,允许同时有多个读者读访问共享资源,只有写者才需要独占资源。相比互斥机制,读写机制由于允许多个读者同时读访问共享资源,进一步提高了多线程的并发度。

      go build --race 可以检查代码里是不是有重复修改数据的点

    互斥锁例子:

      

    package main
    
    import (
    	"fmt"
    	"math/rand"
    	"sync"
    	"time"
    )
    
    var lock sync.Mutex //互斥锁
    
    func testMutx(){
    
    	//go build --race 查看有没有竞争关系,编译后执行返回
    	//互斥锁同一时间只有一个执行
    	var a map[int]int
    	a = make(map[int]int,100)
    
    	a[0] = 1
    	a[1] = 2
    	a[2] = 3
    	a[3] = 4
    	for i := 0; i < 50 ; i++{
    		go func(b map[int]int){
    			lock.Lock() //多个goroutine同时修改同一份数据需要互斥锁
    			b[0] = rand.Intn(100)
    			lock.Unlock()
    		}(a)
    	}
    	lock.Lock()
    	fmt.Println(a)
    	lock.Unlock()
    
    	time.Sleep(time.Second * 1)
    }
    
    func main() {
    
    	testMutx()
    
    }
    

    读写锁例子:

      sync/atomic 这个方法保证是原子操作(串行)

    package main
    
    import (
    	"sync"
    	//"math/rand"
    	"fmt"
    	"sync/atomic"
    	"time"
    )
    
    var lock sync.Mutex
    var rwlock sync.RWMutex
    //读写锁适用于读多写少的场景
    //设置3秒,读写锁执行22万次,互斥锁执行2292次
    //go get xx下载第三方包,默认会下载到gopath的src下
    
    func testRwlock(){
    	var a map[int]int
    	a = make(map[int]int)
    	var count int32
    	a[0] = 10
    	a[1] = 10
    	a[2] = 10
    	a[3] = 10
    	a[4] = 10
    
    	for i := 0; i < 2 ; i++{
    		go func(b map[int]int){
    			//lock.Lock()
    			rwlock.Lock()
    			//b[0] = rand.Intn(100)
    			time.Sleep(3 * time.Millisecond)
    			rwlock.Unlock()
    			lock.Unlock()
    		}(a)
    	}
    
    	for i := 0; i < 100 ; i++{
    		go func(b map[int]int){
    			//lock.Lock()
    			for {
    				//lock.Lock()
    				rwlock.RLock()
    				//fmt.Println(a)
    				time.Sleep(time.Millisecond)
    				rwlock.RUnlock()
    				//lock.Unlock()
    				atomic.AddInt32(&count,1) //atomic原子操作,相当于串行
    			}
    		}(a)
    	}
    
    	time.Sleep(time.Second * 3)
    	fmt.Println(atomic.LoadInt32(&count)) //原子操作
    
    }
    
    func main() {
    	testRwlock()
    }
    

      

    练习

      实现一个冒泡排序:  

      冒泡排序定义:

        它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。
        这个算法的名字由来是因为越大的元素会经由交换慢慢“浮”到数列的顶端,故名“冒泡排序”。
    package main
    
    import "fmt"
    //切片是变长,数组是固定长度
    //冒泡循环两层循环,第二个每次都从后往前确定一个值
    func bsSort(a []int){
    	for i := 0;i < len(a);i++{
    		//每个元素执行一个冒泡
    		for j := 1; j < len(a) - i;j++{
    			if (a[j] < a[j-1]){ //当前值 小于 上一个值就交换位置
    				a[j],a[j-1] = a[j-1],a[j]
    			}
    			fmt.Println(a)
    		}
    	}
    
    }
    
    func main() {
    	slice := []int{8,7,5,4,3,10,15} //切片
    	bsSort(slice)
    	fmt.Println("finally: ",slice)
    }
    
    
    /*
    [7 8 5 4 3 10 15]
    [7 5 8 4 3 10 15]
    [7 5 4 8 3 10 15]
    [7 5 4 3 8 10 15]
    [7 5 4 3 8 10 15]
    [7 5 4 3 8 10 15]
    [5 7 4 3 8 10 15]
    [5 4 7 3 8 10 15]
    [5 4 3 7 8 10 15]
    [5 4 3 7 8 10 15]
    [5 4 3 7 8 10 15]
    [4 5 3 7 8 10 15]
    [4 3 5 7 8 10 15]
    [4 3 5 7 8 10 15]
    [4 3 5 7 8 10 15]
    [3 4 5 7 8 10 15]
    [3 4 5 7 8 10 15]
    [3 4 5 7 8 10 15]
    [3 4 5 7 8 10 15]
    [3 4 5 7 8 10 15]
    [3 4 5 7 8 10 15]
    finally:  [3 4 5 7 8 10 15]
    */
    

     python实现一个冒泡排序:

    L = [3, 5, 6, 7, 8, 1, 2]
    for i in range(len(L)-1):
        for j in range(len(L)-1-i):
            if L[j] >L[j+1]:
                L[j], L[j+1] = L[j+1], L[j]
    print(L)  

      实现一个选择排序(从小到大)

    package main
    
    import "fmt"
    
    //选择排序
    func selectSort(a []int){
         //先选出最小的放到最左面,在一次选择 for i := 0;i < len(a);i++{ //循环整个序列 var min int = i for j:= i + 1; j < len(a);j++{ //循环从1:最后的序列 if (a[min] > a[j]){ //a[min] > a[j] m为当前值 大于下一个值 min = j } fmt.Println(a) } a[i],a[min] = a[min],a[i] //选出来的最小值和当前值交换 } } func main() { slice := []int{8,7,5,4,3,10,15} //切片 selectSort(slice) fmt.Println("finally: ",slice) } /* [8 7 5 4 3 10 15] [8 7 5 4 3 10 15] [8 7 5 4 3 10 15] [8 7 5 4 3 10 15] [8 7 5 4 3 10 15] [8 7 5 4 3 10 15] [3 7 5 4 8 10 15] [3 7 5 4 8 10 15] [3 7 5 4 8 10 15] [3 7 5 4 8 10 15] [3 7 5 4 8 10 15] [3 4 5 7 8 10 15] [3 4 5 7 8 10 15] [3 4 5 7 8 10 15] [3 4 5 7 8 10 15] [3 4 5 7 8 10 15] [3 4 5 7 8 10 15] [3 4 5 7 8 10 15] [3 4 5 7 8 10 15] [3 4 5 7 8 10 15] [3 4 5 7 8 10 15] finally: [3 4 5 7 8 10 15] */

      实现一个插入排序(从小到大)

    package main
    
    import "fmt"
    
    //插入排序 从一个有序的列表里把一个元素插入进去
    func insertSort(a []int){ //一个数组是有序的,另外一个数组是无序的,无序的要去有序里比大小
    	for i := 1;i < len(a);i++{ //这是遍历无序的列表
    		for j := i; j > 0 ;j--{ //这是遍历有序的列表 进行比较插入,无序列表越来越少 ,有序的列表是插入形式,依次增加
    			if (a[j] > a[j-1]){ //当前值 大于下一个值就终止
    				break
    			}
    			a[j],a[j-1] = a[j-1],a[j]
    			fmt.Println(a)
    		}
    	}
    
    }
    
    func main() {
    	slice := []int{8,7,5,4,3,10,15} //切片
    	insertSort(slice)
    	fmt.Println("finally: ",slice)
    }
    
    /*
    7 8 5 4 3 10 15]
    [7 5 8 4 3 10 15]
    [5 7 8 4 3 10 15]
    [5 7 4 8 3 10 15]
    [5 4 7 8 3 10 15]
    [4 5 7 8 3 10 15]
    [4 5 7 3 8 10 15]
    [4 5 3 7 8 10 15]
    [4 3 5 7 8 10 15]
    [3 4 5 7 8 10 15]
    finally:  [3 4 5 7 8 10 15]
    
    */
    

      实现一个快速排序(从小到大)

    package main
    
    import "fmt"
    //快速排序
    func qSort(a []int,left,right int){
    	//选中一个值左面的比他小,右面的比他大
    	if left >= right{ //判断传入是否合法
    		return
    	}
    
    	val := a[left]
    	k := left
    
    	for i := left + 1 ; i <= right;i++{ //循环当前序列,即使被切割也是切割序列
    		if a[i] < val{//val是中间值,如果比val小就要放到val的左面
    			a[k] = a[i] //将小的数与中间值的位置交换
    			a[i] = a[k+1]//在将小的位置,与k的下一位交换
    			a[k+1] = val //将k+1的位置赋值给中间值
    			k++ //最终更换了一个位置所以要+1
    		}
    
    	}
    	//a[k] = val
    	qSort(a,left,k-1) //在左面再次排序
    	qSort(a,k+1,right)//右面再次排序递归
    }
    
    func main() {
    
    	slice := []int{8,7,5,4,3,10,15,20,2} //切片
    	qSort(slice,0,len(slice)-1)
    	fmt.Println("finally: ",slice)
    
    
    }
    
    /*
    finally:  [2 3 4 5 7 8 10 15 20]
    */
    

      

     

    补充:

      1、有些时候需要进行多个赋值操作,由于Go里面没有,操作符,那么可以使用平行赋值i, j = i+1, j-1 

        2、关于Slice定义:

        当切片的索引范围大于len() 但是小于cap()的值,会读取原数组的 隐藏值进行填充

    package main
    
    import "fmt"
    
    func printSlice(v []int){
    	fmt.Printf("slice is %v , len=(%d),cap=(%d)",v,len(v),cap(v) )
    
    }
    
    func main(){
    	a := []int{1,2,3,4,5,6,7,8,9}
    	printSlice(a)
    
    	b := a[2:6]
    	printSlice(b)
    	fmt.Println("before slice is
    ",b)
    	c := b[4:6]
    	printSlice(c)
    	fmt.Println("after slice is
    ",c)
    }
    
    """
    slice is [1 2 3 4 5 6 7 8 9] , len=(9),cap=(9)
    slice is [3 4 5 6] , len=(4),cap=(7)
    before slice is [3 4 5 6]
    slice is [7 8] , len=(2),cap=(3)
    after slice is [7 8] 正常应该认为是7 但是有隐藏值 8,9,10
    
    """
    

        

  • 相关阅读:
    用JavaScript往DIV动态添加内容
    【转】javascript入门系列演示·三种弹出对话框的用法实例
    ASP.Net:Table类的使用
    vs2010设置 "行号显示"
    HTML相对路径 当前目录、上级目录、根目录、下级目录表示法
    【转】算法基础(二):栈的应用 --- 迷宫解题
    【转】CSS中怎么让DIV居中
    【转】如何让DIV水平和垂直居中
    SQL : 在SQL Server 2008(Or Express)中如何Open并编辑数据表【转】
    SQL2005中设置自动编号字段【转】
  • 原文地址:https://www.cnblogs.com/liujiliang/p/8997929.html
Copyright © 2020-2023  润新知