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 =
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()
}
golang(4):函数 & 数组 & 切片 & map & 锁的更多相关文章
- Go语言学习之4 递归&闭包&数组切片&map&锁
主要内容: 1. 内置函数.递归函数.闭包2. 数组与切片3. map数据结构4. package介绍 5. 排序相关 1. 内置函数.递归函数.闭包 1)内置函数 (1). close:主要用来关闭 ...
- Go语言学习笔记(三)数组 & 切片 & map
加 Golang学习 QQ群共同学习进步成家立业工作 ^-^ 群号:96933959 数组 Arrays 数组是同一种数据类型的固定长度的序列. 数组是值类型,因此改变副本的值,不会改变本身的值: 当 ...
- go语言学习-数组-切片-map
数组 go语言中数组的特点: 数组的长度是固定的,并且长度也是数组类型的一部分 是值类型,在赋值或者作为参数传递时,会复制整个数组,而不是指针 定义数组的语法: var arr1 = [5]int{1 ...
- 四、golang内置函数、递归、闭包、数组切片和map
一.总体内容 1.内置函数.递归函数.闭包 2.数组和切片 3.map数据结构 4.package介绍 一.内置函数 注意:值类型用new来分配内存,引用类型用make来分配内存 1.close:主要 ...
- go:内置函数 | 闭包 | 数组 | 切片 | 排序 | map | 锁
内置函数 1.close: 主要是用来关闭channel 2.len:用来求长度,比如string.array.slice.map.channel 3.new与make都是用来分配内存 new用来分配 ...
- 2、Golang基础--包的使用、if-else语句、循环、switch语句、数组、切片、可变函数参数、map类型
1 包的使用 // 为了便于组织代码,同一种类型的代码,写在同一个包下,便于管理 // 定义包 -新建一个文件夹 -内部有很多go文件 -在每个go文件的第一行,都要声明包名,并且包名必须一致 -在一 ...
- golang学习笔记 ---数组与切片
数组: golang数组包含的每个数据称为数组元素(element),数组包含的元素个数被称为数组长度(length). golang数组的长度在定义后不可更改,并且在声明时可以是一个常量或常量表达式 ...
- [golang note] 数组切片
数组 √ golang数组包含的每个数据称为数组元素(element),数组包含的元素个数被称为数组长度(length). √ golang数组的长度在定义后不可更改,并且在声明时可以是一个常量或常量 ...
- Golang 数组 切片 字典 基本知识点
数组 数组的声明 var arrayName [arraySize]dataType eg: var array1 [5]int 在声明数组时,必须指定数组名,数组长度和数组元素的类型. 数组的初始化 ...
随机推荐
- uniapp导航栏自定义按钮及点击事件
本文链接:https://blog.csdn.net/qq_33807889/article/details/89945674第一步:显示按钮假设页面名称为:AddSort 在pages.json中找 ...
- 用python绘制趋势图
import matplotlib.pyplot as plt #plt用于显示图片 import matplotlib.image as mping #mping用于读取图片 import date ...
- PHP中获取数组中单列的值
PHP中获取数组中单列的值如下: 利用PHP中的数组函数 array_column():返回数组中某个单列的值.(PHP 5.5+适用) 语法: array_column(array,column_k ...
- ipv4 ipv6 求字符串和整数一一映射的算法 AmazonOrderId
字符串和整数一一映射的算法 公司每人的英文名不同,现在给每个英文名一个不同的数字编号,怎么设计? 走ipv4/6 2/32 2/128就够了,把“网段”概念对应到“表或库”,ip有a_e5类,这概念 ...
- Android中图片的三级缓存
为什么要使用三级缓存 如今的 Android App 经常会需要网络交互,通过网络获取图片是再正常不过的事了 假如每次启动的时候都从网络拉取图片的话,势必会消耗很多流量.在当前的状况下,对于非wifi ...
- java线程锁之synchronized
声明:该博客参考https://www.cnblogs.com/kaituorensheng/p/10079916.html,感谢哥们. 1.Sync.java package com.cn.comm ...
- 史上最全SVN 教程
以下博文引用<https://blog.csdn.net/u013067756/article/details/73302758>,再此仅供学习和参考. Svn是什么? SVN(全称Sub ...
- 阶段3 3.SpringMVC·_04.SpringMVC返回值类型及响应数据类型_4 响应之返回值是ModelAndView类型
ModelAndView是SpringMvc提供的一个对象 ModelAndView底层源码用也是ModelMap.ModelMap实现过Model的接口 ModelAndView可以直接new出来. ...
- Python深入学习之特殊方法与多范式
Python深入学习之特殊方法与多范式 Python一切皆对象,但同时,Python还是一个多范式语言(multi-paradigm),你不仅可以使用面向对象的方式来编写程序,还可以用面向过程的方式来 ...
- Spring集成CXF获取HttpServletRequest,HttpServletResponse
最近的项目中,在Spring继承CXF中要用到request来获取IP,所以先要获取到HttpServletRequest对象,具体方法如下: 1.配置文件: <jaxrs:server id= ...