1. 数组
数组是一个由固定长度的特定类型元素组成的序列,一个数组可以由零个或多个元素组成。因为数组的长度是固定的,所以在Go语言中很少直接使用数组
声明语法:
var 数组变量名 [元素数量]Type
package main
import (
"fmt"
)
func main() {
var a [3]int // 定义三个整数的数组,仅是定义,默认值是填0
fmt.Println(a[0]) // 打印第一个元素
fmt.Println(a[len(a)-1]) // 打印最后一个元素
// 打印索引和元素
for i, v := range a {
fmt.Printf("%d %d\n", i, v)
}
for _, v := range a {
fmt.Printf("%d\n", v)
}
var q = [...]int{
1, 2, 3}
var r = [3]int{
1, 2}
fmt.Println(q[1]) // "2"
fmt.Println(r[2]) // "0"
k := [...]int{
1, 2, 3}
v := [...]int{
2, 3, 4}
//m := [...]int{1, 2, 3, 4}
fmt.Println(k == v) //false
//fmt.Println(k == m) //不同类型无法进行比较
var teams [3]string
teams[0] = "hello"
teams[1] = "go"
teams[2] = "!"
for i, v := range teams {
fmt.Printf("序号: %d 值: %s\n", i, v)
}
}
2. 切片
2.1 定义切片
// 声明字符串切片
var strList []string
// 声明整型切片
var numList []int
// 声明一个空切片
var numListEmpty = []int{
}
// 输出3个切片
fmt.Println(strList, numList, numListEmpty)
// 输出3个切片大小
fmt.Println(len(strList), len(numList), len(numListEmpty))
// 切片判定空的结果
fmt.Println(strList == nil)
fmt.Println(numList == nil)
fmt.Println(numListEmpty == nil)
make构造切片
a := make([]int, 2)
b := make([]int, 2, 10)
fmt.Println(a, b)
fmt.Println(len(a), len(b))
2.2 为切片新增元素
package main
import "fmt"
var numbers []int
func main() {
var a []int
x := []int{
1, 2, 3}
a = append(a, 1) // 追加1个元素
a = append(a, 1, 2, 3) // 追加多个元素, 手写解包方式
a = append(a, x...) // 追加一个切片, 切片需要解包
fmt.Println(a)
deal()
var b = []int{
1, 2, 3}
b = append([]int{
0}, b...) // 在开头添加1个元素
fmt.Println(b)
b = append([]int{
-3, -2, -1}, b...) // 在开头添加1个切片
fmt.Println(b)
}
func deal() {
for i := 0; i < 10; i++ {
numbers = append(numbers, i)
fmt.Printf("len: %d cap: %d pointer: %p\n", len(numbers), cap(numbers), numbers)
}
}
2.3 切片复制
copy作用前面的切片元素,以小的切片元素为复制对象
package main
import "fmt"
func main() {
slice1 := []int{
1, 2, 3, 4, 5}
slice2 := []int{
5, 4, 3}
copy(slice2, slice1) // 只会复制slice1的前3个元素到slice2中
fmt.Println(slice1) //[1 2 3 4 5]
fmt.Println(slice2) //[1 2 3]
slice2 = []int{
7, 8, 9}
copy(slice1, slice2) // 只会复制slice2的3个元素到slice1的前3个位置
fmt.Println(slice1) //[7 8 9 4 5]
}
package main
import "fmt"
func main() {
// 设置元素数量为1000
const elementCount = 1000
// 预分配足够多的元素切片
srcData := make([]int, elementCount)
// 将切片赋值
for i := 0; i < elementCount; i++ {
srcData[i] = i
}
// 引用切片数据
refData := srcData
// 预分配足够多的元素切片
copyData := make([]int, elementCount)
// 将数据复制到新的切片空间中
copy(copyData, srcData)
// 修改原始数据的第一个元素
srcData[0] = 999
// 打印引用切片的第一个元素
fmt.Println(refData[0])
// 打印复制切片的第一个和最后一个元素
fmt.Println(copyData[0], copyData[elementCount-1])
// 复制原始数据从4到6(不包含)
copy(copyData, srcData[4:6])
for i := 0; i < 5; i++ {
fmt.Printf("%d ", copyData[i])
}
}
代码说明如下:
- 第 8 行,定义元素总量为 1000。
- 第 11 行,预分配拥有 1000 个元素的整型切片,这个切片将作为原始数据。
- 第 14~16 行,将 srcData 填充 0~999 的整型值。
- 第 19 行,将 refData 引用 srcData,切片不会因为等号操作进行元素的复制。
- 第 22 行,预分配与 srcData 等大(大小相等)、同类型的切片 copyData。
- 第 24 行,使用 copy() 函数将原始数据复制到 copyData 切片空间中。
- 第 27 行,修改原始数据的第一个元素为 999。
- 第 30 行,引用数据的第一个元素将会发生变化。
- 第 33 行,打印复制数据的首位数据,由于数据是复制的,因此不会发生变化。
- 第 36 行,将 srcData 的局部数据复制到 copyData 中。
- 第 38~40 行,打印复制局部数据后的 copyData 元素。
2.4 切片删除元素
2.4.1 开头元素
特性
a := []int{
1, 2, 3}
a = a[1:] // 删除开头1个元素
a = a[N:] // 删除开头N个元素
append
a = []int{
1, 2, 3}
a = append(a[:0], a[1:]...) // 删除开头1个元素
a = append(a[:0], a[N:]...) // 删除开头N个元素
copy
a = []int{
1, 2, 3}
a = a[:copy(a, a[1:])] // 删除开头1个元素
a = a[:copy(a, a[N:])] // 删除开头N个元素
2.4.2 中间删除
a = []int{
1, 2, 3, ...}
a = append(a[:i], a[i+1:]...) // 删除中间1个元素
a = append(a[:i], a[i+N:]...) // 删除中间N个元素
a = a[:i+copy(a[i:], a[i+1:])] // 删除中间1个元素
a = a[:i+copy(a[i:], a[i+N:])] // 删除中间N个元素
2.4.3 末尾删除
a := []int{
1, 2, 3}
a = a[:len(a)-1] // 删除尾部1个元素
a = a[:len(a)-N] // 删除尾部N个元素
2.4.4 指定元素删除
package main
import "fmt"
func main() {
list1 := []int{
1, 2, 3, 3, 5, 4}
list2 := []int{
1, 2, 3, 3, 5, 4}
list1 = DeleteSlice3(list1, 3)
fmt.Println(list1)
list3 := DeleteSlice2(list2, 3)
fmt.Println(list3)
}
// DeleteSlice3 位移法
func DeleteSlice3(a []int, elem int) []int {
j := 0
for _, v := range a {
if v != elem {
a[j] = v
fmt.Println(a)
j++
}
}
return a[:j]
}
// DeleteSlice2 拷贝法
func DeleteSlice2(a []int, elem int) []int {
tmp := make([]int, 0, len(a))
for _, v := range a {
if v != elem {
tmp = append(tmp, v)
}
}
return tmp
}
位移法输出:
[1 2 3 3 5 4]
[1 2 3 3 5 4]
[1 2 5 3 5 4]
[1 2 5 4 5 4]
[1 2 5 4]
解析:把后面不等于3的元素提前,然后根据最后j的长度来返回需要取多长,同时把3元素排除
拷贝法输出:
[1]
[1 2]
[1 2 5]
[1 2 5 4]
[1 2 5 4]
解析:这个就很好理解了,创建一个空的切片,值不等就添加进去
3. 映射
3.1 map概念
package main
import "fmt"
func main() {
map1 := make(map[int]int)
map1 = map[int]int{
1: 3, 2: 4}
fmt.Println(map1)
for i, v := range map1 {
fmt.Printf("键是: %d 值是: %d\n", i, v)
}
}
3.2 遍历map
package main
import "fmt"
func main() {
slice1 := make([]int, 0)
slice1 = append(slice1, 1, 2, 3, 4)
map1 := make(map[int]int)
for i, v := range slice1 {
map1[i] = v
}
fmt.Println(map1)
}
注明:
for i, v := range 可输出键值
如果只需要键:
for i := range
如果只需要值:
for _, v := range
3.3 map转slice排序
package main
import (
"fmt"
"sort"
)
func main() {
scene := make(map[string]int)
// 准备map数据
scene["route"] = 66
scene["brazil"] = 4
scene["china"] = 960
// 声明一个切片保存map数据
var sceneList []string
// 将map数据遍历复制到切片中
for k := range scene {
sceneList = append(sceneList, k)
}
// 对切片进行排序
sort.Strings(sceneList) //对字符串进行排序
// 输出
fmt.Println(sceneList)
}
3.4 删除map中的元素
package main
import (
"fmt"
)
func main() {
scene := make(map[string]int)
// 准备map数据
scene["route"] = 66
scene["brazil"] = 4
scene["china"] = 960
delete(scene, "brazil")
for k, v := range scene {
fmt.Println(k, v)
}
}
ps: 使用字典就是用make函数重新创个新的即可,其余的交给GC回收机制
3.5 并发环境使用Map
sync.Map 有以下特性:
- 无须初始化,直接声明即可。
- sync.Map 不能使用 map 的方式进行取值和设置等操作,而是使用 sync.Map 的方法进行调用,Store 表示存储,Load 表示获取,Delete 表示删除。
- 使用 Range 配合一个回调函数进行遍历操作,通过回调函数返回内部遍历出来的值,Range 参数中回调函数的返回值在需要继续迭代遍历时,返回 true,终止迭代遍历时,返回 false。
代码演示如下:
package main
import (
"fmt"
"sync"
)
func main() {
var scene sync.Map
// 将键值对保存到sync.Map
scene.Store("greece", 97)
scene.Store("london", 100)
scene.Store("egypt", 200)
// 从sync.Map中根据键取值
fmt.Println(scene.Load("london"))
// 根据键删除对应的键值对
scene.Delete("london")
// 遍历所有sync.Map中的键值对
scene.Range(func(k, v interface{
}) bool {
fmt.Println("iterate:", k, v)
return true
})
}
代码说明如下:
- 第 10 行,声明 scene,类型为 sync.Map,注意,sync.Map 不能使用 make 创建。
- 第 13~15 行,将一系列键值对保存到 sync.Map 中,sync.Map 将键和值以 interface{} 类型进行保存。
- 第 18 行,提供一个 sync.Map 的键给 scene.Load() 方法后将查询到键对应的值返回。
- 第 21 行,sync.Map 的 Delete 可以使用指定的键将对应的键值对删除。
- 第 24 行,Range() 方法可以遍历 sync.Map,遍历需要提供一个匿名函数,参数为 k、v,类型为 interface{},每次 Range() 在遍历一个元素时,都会调用这个匿名函数把结果返回。
4. 列表
列表创建与传值
package main
import (
"container/list"
"fmt"
)
func main() {
l := list.New()
l.PushFront("hello")
l.PushBack("world")
element := l.PushBack("wo")
l.InsertBefore("baby", element)
l.InsertAfter("aiNi", element)
l.Remove(element)
for i := l.Front(); i != nil; i = i.Next() {
fmt.Println(i.Value)
}
}
5. 空值与零值
指针、切片、映射、通道、函数和接口的零值则是 nil
5.1 nil标识符不能比较
package main
import (
"fmt"
)
func main() {
fmt.Println(nil==nil)
}
运行结果: 未定义操作
PS D:\code> go run .\main.go
#command-line-arguments
.\main.go:8:21: invalid operation: nil == nil (operator == not defined on nil)
5.2 nil不是关键字和保留字
以下编写是可以通过编译的,但不推荐这么做
var nil = errors.New("my god")
5.3 nil没有默认类型
package main
import (
"fmt"
)
func main() {
fmt.Printf("%T", nil)
print(nil)
}
输出:
#command-line-arguments
.\main.go:7:8: use of untyped nil in argument to print
5.4 不同类型 nil 的指针是一样的
package main
import (
"fmt"
)
func main() {
var arr []int
var num *int
fmt.Printf("%p\n", arr)
fmt.Printf("%p", num)
}
输出:
0x0
0x0
Process finished with the exit code 0
5.5 nil是map、slice、pointer、channel、func、interface 的零值
package main
import (
"fmt"
)
func main() {
var m map[int]string
var ptr *int
var c chan int
var sl []int
var f func()
var i interface{
}
fmt.Printf("%#v\n", m)
fmt.Printf("%#v\n", ptr)
fmt.Printf("%#v\n", c)
fmt.Printf("%#v\n", sl)
fmt.Printf("%#v\n", f)
fmt.Printf("%#v\n", i)
}
输出:
PS D:\code> go run .\main.go
map[int]string(nil)
(*int)(nil)
(chan int)(nil)
[]int(nil)
(func())(nil)
5.6 不同类型的nil值占用的内存大小可能不一样
package main
import (
"fmt"
"unsafe"
)
func main() {
var p *struct{
}
fmt.Println( unsafe.Sizeof( p ) ) // 8
var s []int
fmt.Println( unsafe.Sizeof( s ) ) // 24
var m map[int]bool
fmt.Println( unsafe.Sizeof( m ) ) // 8
var c chan string
fmt.Println( unsafe.Sizeof( c ) ) // 8
var f func()
fmt.Println( unsafe.Sizeof( f ) ) // 8
var i interface{
}
fmt.Println( unsafe.Sizeof( i ) ) // 16
}
输出:
PS D:\code> go run .\main.go
8
24
8
8
8
16