内置函数

// 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 =
fmt.Println(*j)
} // 运行结果如下:
[root@NEO example01_new]# go run main/main.go 0xc0000160b0 [root@NEO example01_new]# // append 示例:
package main import "fmt" func main(){
var a []int // [] 中不写数字表示是一个切片 slice
a = append(a,,) // append 中也是可变参数
fmt.Println(a) a = append(a,a...) // 添加一个切片;a...表示 切片a 中的所有元素
fmt.Println(a)
} // 运行结果如下:
[root@NEO example01_append]# go run main/main.go
[ ]
[ ]
[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 :=
b := /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 == {
return
} return calc(n-) * n
} func main() {
n := calc()
fmt.Println(n)
} // 示例2:斐波那契数
package main import "fmt" func fab(n int) int {
if n <= {
return
}
return fab(n-)+fab(n-)
} func main(){
n := fab()
fmt.Println(n)
}

递归的设计原则

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

闭包

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

// 示例代码:
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()) // 此时 x 的值为默认的 0
fmt.Println("f(10)",f()) // 此时 x 的值变成了 1
fmt.Println("f(100)",f()) // 此时 x 的值变成了 10
} // 运行结果如下:
[root@NEO example03_closure]# go run main/main.go
f()
f()
f()
[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 := ; i < len(a); i++ {
} for index, v := range a {
} // 5. 访问越界,如果下标在数组合法范围之外,则触发访问越界,会panic
// 6. 数组是值类型,因此改变副本的值,不会改变本身的值
arr2 := arr1
arr2[] =

数组的初始化

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

多维数组

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

多维数组遍历

// 示例代码:
package main import "fmt" func test(){
var arr [][]int = [...][]int{{,,,,},{,,,,}} 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
(,)= (,)= (,)= (,)= (,)=
(,)= (,)= (,)= (,)= (,)=
[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[] = // 修改 切片 a 的元素的值后,函数外 a 的值也会改变
} func testSlice(){
var s []int = []int{,,}
modifySlice(s) // 切片是引用类型,是数组的一个引用(可理解成切片是一个地址);此函数传了一个引用(引用中包含指针);可理解成给该函数传了一个地址
fmt.Println(s)
} func main(){
testSlice()
} // 运行结果如下:
[root@NEO example05_slice_ref_type]# go run main/main.go
[ ] // 函数外切片的值也发生了改变
[root@NEO example05_slice_ref_type]# // 切片是引用类型的示例2:
package main import "fmt" func testSliceRefArr(){
var a = []int{,,} // 声明并初始化一个数组
b := a[:] // 声明并初始化一个切片 fmt.Printf("%p\n",b) // %p 表示十六进制表示,可用于打印一个地址
fmt.Printf("%p\n",&a[]) // 数组 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 []int = [...]int{,,,,} // 声明并初始化一个数组 slice = arr[:] // 对切片初始化; 顾首不顾尾;可简写成 arr[2:]
fmt.Println(slice)
fmt.Println("len:",len(slice))
fmt.Println("cap:",cap(slice)) slice = slice[:]
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
[ ]
len:
cap:
len:
cap:
[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)-]

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

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

用append内置函数操作切片

// 示例:
slice = append(slice, )
var a = []int{,,}
var b = []int{,,}
a = append(a, b…) // b... 表示 切片 b 中的所有元素 // append 的示例:
package main import "fmt" func testSlice(){
var a []int = [...]int{,,,,}
b := a[:] fmt.Printf("b=%p a[1]=%p\n",b,&a[]) // 此时切片 b 的地址和 数组a第一个元素的地址是一样的 b[] =
fmt.Println("before a:",a) // 此时 改变切片 b 的值 数组a 也会改变 b = append(b,)
b = append(b,)
b = append(b,)
b = append(b,)
b = append(b,)
b = append(b,) // 添加了5个元素后,已经超过了切片 b 的容量 fmt.Println(b)
fmt.Printf("b=%p a[1]=%p\n",b,&a[]) // 此时切片 b 的地址和 数组a第一个元素的地址就不再一样;因为在声明并定义切片b时没有超过b的容量,此时b指向原来的数组,当超过b的容量后,系统会重新开辟一块内存,并把切片b原有的值放到新开辟的内存中,然再把追加的内容放进去 b[] =
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[]=0xc000014158
before a: [ ] // a 中对应的值也发生了改变
[ ]
b=0xc000062080 a[]=0xc000014158
after a: [ ] // 此时 a 中的值并没有改变
[root@NEO example05_slice_beyond_cap]#

切片resize

var a = []int {,,,}
b := a[:]
b = b[:]

切片拷贝:copy()

s1 := []int{,,,,}
s2 := make([]int, )
copy(s2, s1) // 把 切片 s1 拷贝到 切片 s2 中;拷贝不会扩容 // 示例代码:
package main import "fmt" func testCopy(){
var a []int = []int{,,,,}
b := make([]int,)
c := make([]int,) 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
[ ]
[]
[root@NEO example05_slice_copy]#

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

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

string与slice

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

示例代码:

// 示例代码如下:
package main import "fmt" func strModifyByte(){
str := "hello world"
s := []byte(str) // 把字符串转换为 byte 数组
fmt.Println(s) s[] = 'o'
str = string(s) // 再把 byte 数组转换为字符串
fmt.Println(str)
} func strChangeRune(){
str := "hello 中国"
s := []rune(str) // 把字符串转换为 字符 数组 (可以转换中文)
fmt.Println(s) s[] = '大' // 字符类型 一定要用 单引号
str = string(s) // 把 字符数组 转换成字符串
fmt.Println(str)
} func main(){
strModifyByte()
strChangeRune()
} // 运行结果如下:
[root@NEO example05_string_change]# go run main/main.go
[ ]
oello world
[ ]
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{, , , , , }
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{, , , , , }
sort.Ints(a[:]) // 搜索前需要先将数组排序;如果没有先排序,搜索到的索引值也是排序后的索引值
index := sort.SearchInts(a[:],) // 传入切片
fmt.Println(index)
} func main(){
testSortInt()
testSortStrings()
testSortFloat()
testSearchInts()
} // 运行结果如下:
[root@NEO example05_sort_search]# go run main/main.go
[ ]
[A abc b eeee efg]
[0.6 0.8 2.3 28.2 392342.2] [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, )
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,) // 初始化
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,) // 声明并初始化
fmt.Println(c) e := map[string]string{
"key":"val",
}
fmt.Println(e) d := make(map[string]map[string]string,) // map 中嵌套 map;此处的map声明并初始化只是对最外层的 map 进行了 初始化,里面的 map 只进行了声明,但并未初始化
d["key1"] = make(map[string]string,) // 在对里面那个 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"] = ""
} else {
a["zhangsan"] = make(map[string]string) // map 中不存在 "zhangsan" 这个 key,则在对其操作前要先对其初始化
a["zhangsan"]["nickname"] = "胖哥"
a["zhangsan"]["passwd"] = ""
}
} func main(){
testMap() a := make(map[string]map[string]string,)
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:]]
[root@NEO example06_map_var]#

map是引用类型

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

slice of map

items := make([]map[int][int], )
For i := ; i < ; 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,) a[] = "hello"
a[] = "中国"
a[] = "aaa"
a[] = "abc"
a[] = "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
中国
hello
abc
aaa
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, ) a[] =
a[] =
a[] =
a[] =
a[] = for i := ; i < ; i++ {
go func(b map[int]int) { // 匿名函数
lock.Lock()
b[] = rand.Intn() // 写操作;中间的代码加锁
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, )
var count int32
a[] =
a[] =
a[] =
a[] =
a[] = for i := ; i < ; i++ {
go func(b map[int]int) {
rwLock.Lock()
b[] = rand.Intn()
time.Sleep( * time.Millisecond)
rwLock.Unlock()
}(a)
} for i := ; i < ; i++ {
go func(b map[int]int) {
for {
rwLock.RLock()
time.Sleep(time.Millisecond)
//fmt.Println(a)
rwLock.RUnlock()
atomic.AddInt32(&count, ) // 修改公共数据 count : 进行原子性 写操作
}
}(a)
}
time.Sleep(time.Second * )
fmt.Println(atomic.LoadInt32(&count)) // 读取进行原子性的数据
} func main() {
//testMap()
testRWLock()
}

最新文章

  1. EF 数据初始化
  2. angularjs不同页面间参数的传递
  3. MongoDB的C#官方驱动InvalidOperationException异常的解决办法
  4. LeakCanary中英文文档+使用例子
  5. Js里面的强制类型转换
  6. 【UML】如何记忆UML类图的画法
  7. Sonatype Nexus高级配置
  8. Linux 上的常用文件传输方式介绍与比较
  9. mapreduce程序编写(WordCount)
  10. Xcode7.01相对于底版本的变动小结
  11. JavaEE(24) - JAAS开发安全的应用
  12. UVA 11027 - Palindromic Permutation
  13. 南理第八届校赛同步赛-C count_prime//容斥原理
  14. 【Python】将python3.6软件的py文件打包成exe程序
  15. centos7 lldb 调试netcore应用的内存泄漏和死循环示例(dump文件调试)
  16. Linuxer-&amp;quot;Linux开发人员自己的媒体&amp;quot;第五月稿件和赠书名单
  17. [kx]宇宙-银河
  18. .netcore部署centos
  19. MyBatis的动态插入语句(经常报‘无效的列类型’)
  20. 通达OA 在工作流中直接查询表单内容的开发尝试(图文)

热门文章

  1. Python 之 try...except...错误捕捉
  2. ActiveXObject常用方法
  3. ubuntu 18.04 64bit下如何安装安卓虚拟机anbox?
  4. -moz-box-shadow
  5. mysql 安装 和 mysql 远程连接
  6. log4j日志配置文件
  7. spring mvc路径匹配原则
  8. 系统启动热键(Boot Hotkey)
  9. Control的Invoke和BeginInvoke
  10. 数据中心网络中的40GBASE-T