四、golang内置函数、递归、闭包、数组切片和map
一、总体内容1、内置函数、递归函数、闭包
2、数组和切片
3、map数据结构
4、package介绍
一、内置函数
注意:值类型用new来分配内存,引用类型用make来分配内存
1、close:主要用来关闭channel
2、len:用来求长度,比如string、array、slice、map、channel
3、new:用来分配内存,主要用来分配值类型,比如int、struct、浮点型。返回的是指针
代码案例
package main
import(
"fmt"
)
func main(){
var i int
fmt.Println(i)//0
j:=new(int)//返回的是地址,也就是指针
*j=100 //因为是指针,所以要这样赋值
fmt.Println(*j) //100
}
4、make:用来分配内存,主要用来分配引用类型。比如chan、map、slice
5、append:用来追加元素到数组、slice中
package main
import(
"fmt"
)
func main(){
var a [] int
a=append(a,10,20,30)
fmt.Println(a)//
}
合并两个slice
package main
import(
"fmt"
)
func main(){
var a [] int
a=append(a,10,20,30)
a=append(a, a...)//后面的三个点,是展开,这个append是合并
fmt.Println(a)
}
注意这里的三个点的作用是展开
6、panic和recover:用来做错误处理
panic可以快速定位到哪里出错了
捕获异常的原因是因为上线项目之后不能够随便的停止,所以要捕获
package main
import(
"fmt"
"time"
)
func test(){
defer func(){
if err:=recover();err !=nil{ //这里捕获下面的系统的异常
fmt.Println(err) //这里的错误没有指定哪一行出错了。这里可以把堆栈打印出来
//捕获了异常之后下面可以继续写上报警的接口或者写到日志里面
}
}()
b:=0
a:=100/b//这里系统抛了一个异常,上面来捕获
fmt.Println(a)
return
}
func main(){
for {
test()
time.Sleep(time.Second)//这里是休息1秒,参数是一个常量
}
var a [] int
a=append(a,10,20,30)
a=append(a, a...)
fmt.Println(a)//
}
D:\project>go build go_dev/day4/example/example2
D:\project>example2.exe
runtime error: integer divide by zero
runtime error: integer divide by zero。
还可以自己手动的通过pinic来捕获异常
package main
import(
"fmt"
"errors"
)
func initConfig()(err error){ //这里是命名返回值
return errors.New("init config failed") //初始化error这个实例
}
func test(){
err :=initConfig()
if err !=nil{
panic(err) //panic手动的捕获这个异常,这样就能知道程序的哪里出错了
}
return
}
func main(){
test()
var a [] int
a=append(a,10,20,30)
a=append(a, a...)
fmt.Println(a)//
}
D:\project>go build go_dev/day4/example/example2
D:\project>example2.exe
panic: init config failed
goroutine 1 :
main.test()
D:/project/src/go_dev/day4/example/example2/main.go:17 +0xb2
main.main()
D:/project/src/go_dev/day4/example/example2/main.go:24 +0x3b
数组:
var a int:这个是确定的长度,为1
var a [] int :这个是切片
new和make的区别
new之后如果是slice等引用类型必须要用make初始化一下才可以用如下:
package main
import(
"fmt"
)
func test(){
s1:=new([] int) //new了这个指针,必须要用make来初始化一下
fmt.Println(s1) //这个返回来一个指针&[]
s2:=make([] int, 2)//这个后面的2是指定容量
fmt.Println(s2) //这个返回来这个数据类型
*s1=make([] int,5)//因为s1是一个指针(地址),要想用这个指针必须初始化一下
(*s1)=100 //初始化之后给这个值赋值
fmt.Println(s1) //&
s2=100
fmt.Println(s2) //
}
func main(){
test()
}
递归函数
一个函数调用自己,就叫做递归
package main
package main
import (
"fmt"
"time"
)
func recusive(n int){
fmt.Println("hello")
time.Sleep(time.Second)
if n>10{ //这个是递归退出的条件
return
}
recusive(n+1) //这个就是递归的自己调用自己
}
func main(){
recusive(0)
}
例子一:计算阶乘
package main
import (
"fmt"
)
func factor(n
int) int {
if n==1{
return 1
}
return factor(n-1)*n
}
func main(){
a:
=factor(5)
fmt.Println(a)
}
D:\project
>go build go_dev/day4/example/example5
D:\project
>example5.exe
120
View Code 斐波那契数列
package main
import(
"fmt"
)
func fabnaqi(n
int) int{
if nexample6.exe
1
2
3
5
8
13
21
34
55
View Code 闭包:
闭包:是一个函数和与其相关作用域的结合体
下面的匿名函数中的变量和x绑定了
package main
import (
"fmt"
)
//闭包
func Adder() func(int)int{ //这里定义的返回值要和下面的匿名函数一样
//下面的整体才是闭包
var x int //默认为0
return func(d int) int{ //这里匿名函数要和上面返回值类型是一样
x+=d
return x
}
}
func main(){
f:=Adder()
fmt.Println(f(1)) //这里的f就是执行Adder(),然后f(1),这个就是执行匿名函数,并且传入参数
fmt.Println(f(100)) //101
fmt.Println(f(1000)) //1101
}
View Code 例2
package main
import (
"fmt"
"strings"
)
func makeSuffixFunc(suffix string) func (string )string{
return func (name string) string{ //这里的闭包的环境变量是和suffix绑定的
if strings.HasPrefix(name,suffix)==false{
return name+suffix
}
return name
}
}
func main(){
func1:=makeSuffixFunc(".bmp")
func2:=makeSuffixFunc(".jpg")
fmt.Println(func1("test"))
fmt.Println(func2("test"))
}
View Code 数组
1、数组:是同一种数据类型的固定长度的序列
2、数组定义:var a int 比如:var a int 一旦定义长度就不会变了
3、长度是数组类型的一部分,因此var aint和var aint 是不同的类型
4、数组可以通过下标进行访问,下标是从零开始的,最后一个元素下标是len-1
遍历的方法如下:
下面是遍历数组的两种方法
package main
import (
"fmt"
)
func main(){
var a int
a[
0]=100
for i:=0;iexample15
*/
下面是对字符串进行排序
func testString(){
var a=[...]string{"abc","efg","b","A","eee"}
sort.Strings(a[:])
fmt.Println(a)//
}
func main(){
testString()
}
下面是对浮点型进行排序
func testFloat(){
var a=[...]float64{1.1,9.7,2.1}
sort.Float64s(a[:])
fmt.Println(a) //
}
func main(){
testFloat()
}
下面是排序的方法
func testIntsearch(){
var a=[...]int{1,8,38,2,348,4}
sort.Ints(a[:])
index:=sort.SearchInts(a[:],2)
fmt.Println(index) //得到下标为1
}
func main(){
testIntsearch()
}
View Code map数据结构
一、简介
key-value的数据结构
a、声明,声明是不会分配内存的,初始化需要make
var map1 mapvalue type
var a mapstring
var a mapint
var a mapstring
var a mapmapstring
package main
import (
"fmt"
)
func test(){
var a mapstring//声明map,但是这里没有申请内存空间,所以必须初始化
a=make(mapstring,10)//这里初始化,申请内存空间
a["abc"]="efg"
fmt.Println(a)//map
}
func main(){
test()
}
方式二
package main
import (
"fmt"
)
func test(){
a:=make(mapstring,10)//这里声明加上初始化,声明然后分配内存空间
a["abc"]="efg"
fmt.Println(a)//map
}
func main(){
test()
}
方法三:不推荐
package main
import (
"fmt"
)
func test(){
var a mapstring=mapstring{
"key":"value",
}
//var a mapstring//声明map,但是这里没有申请内存空间,所以必须初始化
//a:=make(mapstring,10)//这里初始化,申请内存空间
a["abc"]="efg"
fmt.Println(a)//map
}
func main(){
test()
}
多层map嵌套
func testMap2(){
a:=make(mapmapstring,100)//声明
a["key1"]=make(mapstring)//初始化
a["key1"]["key2"]="abc"
a["key1"]["key3"]="abc"
fmt.Println(a)
}
func main(){
testMap2()
test()
}
View Code map相关的操作
a[“hello”]=”world” 插入和更新
val,ok:=a[“hello”] 查找
for k,v:=range a { 遍历
}
delete (a,”hello”) 删除
len(a) 长度
func test3() {
var a mapstring = mapstring{"hello": "world"}
a
= make(mapstring, 10)
a[
"hello"] = "world"//插入和更新
val,ok:=a["hello"]//查找
if ok{
fmt.Println(val)
}
for k,v :=range a { //遍历
fmt.Println(k,v)
}
}
func main(){
test3()
}
View Code 排序:
map排序,map的排序是无序的:
a、先获取所有key,把key进行排序
b、按照排好的key,进行遍历
package main
import(
"fmt"
"sort"
)
func test(){
var a mapint //声明
a=make(mapint,5) //初始化,加入内存
a=10
a=11
a=10
a=10
a=10
var keys []int //创建一个切片
for k,_:=range a{
keys=append(keys,k)
}
sort.Ints(keys)
for _,v:=range keys{
fmt.Println(v,a)
}
}
func main(){
test()
}
//D:\project>go build go_dev/day4/example/example17
//
//D:\project>example17.exe
//1 10
//2 10
//3 11
//8 10
//18 10
上面思路,由于map是无序的,这里创建一个切片,然后根据切片的方法进行排序
View Code map反转
初始化另外一个map把key、value呼唤即可
package main
import (
"fmt"
)
func test(){
var a map int
var b map string
a=make(mapint,5)
b=make(mapstring,5)
a["abc"]=1
a["efg"]=2
for k,v:=range a{
b=k
}
fmt.Println(a)//map
fmt.Println(b)//map
}
func main(){
test()
}
原理:这里首先声明两个map,并且初始化,然后进行遍历第一个map,然后第二个map直接添加第一个map的v和k即可
包
1、golang中的包
a、golang目前有150个标准的包,覆盖了几乎所有的基础库
b、golang.org有所有包的文档,没事就翻翻
2、线程同步
a、import(“sync”)
b、互斥锁 var mu sync.Mutex,同一时间只能有一个goroute能进去
c、读写锁,var mu sync.RWMutex
线程和协程,只有读操作的时候不用加锁
1、如果有写操作,需要加锁
2、如果有读写的操作,需要加锁
3、如果只有读的操作,不需要加锁
编译的时候—race可以查看是否有竞争
下面是互斥锁的程序
import (
"fmt"
"sync"
"math/rand"
"time"
)
//因为这是一个读写的操作,所以读的时候也要加锁
var lock sync.Mutex
func test(){
var a map int
a=make(mapint,5)
a=10
for i :=0;igo build --race go_dev/day4/example/packagea 这里—race是检测是否有竞争
D:\project>packagea.exe //因为有race所以下面检测没有竞争
map
上面是互斥锁
应用场景:
写比较多,有少量的读。(写多读少)
加锁后,任何其他试图再次加锁的线程会被阻塞,直到当前进程解锁
如果解锁时有一个以上的线程阻塞,那么所有该锁上的线程都被编程就绪状态,
第一个变为就绪状态的线程又执行加锁操作,那么其他的线程又会进入等待。
在这种方式下,只有一个线程能够访问被互斥锁保护的资源。
互斥锁无论读还是写同一时间只有一个协程在执行
View Code 读写锁:
实际就是一种特殊的自旋锁,它把共享资源的访问者划分为读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作
使用场景:
写比较少,有大量的读 (读多写少)
写的时候只有一个goroute去写,但是读的时候所有的goroute去读
读写锁有三种状态:读加锁状态、写加锁状态和不加锁状态
一次只有一个线程可以占有写模式的读写锁,但是多个线程可以同时占有读模式的读写锁。(这也是它能够实现高并发的一种手段)
当读写锁在写加锁模式下,任何试图对这个锁进行加锁的线程都会被阻塞,直到写进程对其解锁。
当读写锁在读加锁模式先,任何线程都可以对其进行读加锁操作,但是所有试图进行写加锁操作的线程都会被阻塞,直到所有的读线程都解锁。
所以读写锁非常适合对数据结构读的次数远远大于写的情况
package main
import (
"fmt"
"sync"
"math/rand"
"time"
)
var rwlock sync.RWMutex //读写锁的定义
func test(){
var a map int
a=make(mapint,5)
//var count int32
a=10
for i:=0;i
页:
[1]