切片Slice
其本身并不是数组,它指向底层的数组
作为变长数组的替代方案,可以关联底层数组的局部或全部
为引用类型
可以直接创建或从底层数组获取生成
使用len()获取元素个数,cap()获取容量
一般使用make()创建
如果多个slice指向相同底层数组,其中一个的值改变会影响全部
make([]T, len, cap)
其中cap可以省略,则和len的值相同
len表示存数的元素个数,cap表示容量
package main
import (
"fmt"
)
func main() {
a := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
fmt.Println(a)
s1 := a[5]
fmt.Println(s1)
s2 := a[5:10] //从索引5开始取到后面的元素
//s2 := a[5:len(a)] //从索引5开始取到后面的元素
//s2 := a[5:] //从索引5开始取到后面的元素
//s3 := a[:5] //取前面5个元素
fmt.Println(s2)
}
package main
import (
"fmt"
)
func main() {
s1 := make([]int, 3, 10) //3:底层数组元素个数 10:数组容量
fmt.Println(len(s1), cap(s1)) //cap()取得数组的容量
}
Slice与底层数组的对应关系
Reslice
Reslice时索引以被slice的切片为准
索引不可以超过被slice的切片的容量cap()值
索引越界不会导致底层数组的重新分配而是引发错误
func main() {
a := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k'}
s1 := a[2:5]
fmt.Println(string(s1))
}
func main() {
a := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k'}
s1 := a[2:5] //Slice类型
fmt.Println(len(s1), cap(s1)) //取出s1的数组长度和容量
s2 := s1[1:3] //Reslice类型
fmt.Println(string(s1))
fmt.Println(string(s2))S
}
Append 函数
可以在slice尾部追加元素
可以将一个slice追加在另一个slice尾部
如果最终长度未超过追加到slice的容量则返回原始slice
如果超过追加到的slice的容量则将重新分配数组并拷贝原始数据
func main() {
s1 := make([]int, 3, 6) //元素3个,容量为6
s1 = append(s1, 1, 2, 6)
fmt.Printf("%p\n", s1) //s1的内存地址
fmt.Printf("%v %p\n", s1, s1) //%v 数据的值
s1 = append(s1, 1, 2, 6)
fmt.Printf("%v %p\n", s1, s1) //%v 数据的值
}
func main() {
a := []int{1, 2, 3, 4, 5}
s1 := a[2:5]
s2 := a[1:3]
fmt.Println(s1, s2)
s1[0] = 9
fmt.Println(s1, s2)
}
func main() {
a := []int{1, 2, 3, 4, 5}
s1 := a[2:5]
s2 := a[1:3]
fmt.Println(s1, s2)
s2 = append(s2, 1, 2, 1, 2, 3, 4, 5, 1, 2, 3)
s1[0] = 9
fmt.Println(s1, s2)
}
copy函数的使用方法
func main() {
s1 := []int{1, 2, 5, 6, 8, 3, 2, 4}
s2 := []int{2, 4, 5}
copy(s2[1:3], s1[3:8]) //s1复制到s2当中
fmt.Println(s2)
}
/*
s2复制s1
*/
func main() {
s1 := []int{1,2,3,4,5}
s2 := s1
//s2 := s1[0:5]
//s2 := s1[:5]
//s2 := s1[:]
fmt.Println(s2)
}
map
类似其它语言中的哈希表或者字典,以key-value形式存储数据
Key必须是支持==或!=比较运算的类型,不可以是函数、map或slice
Map查找比线性搜索快很多,但比使用索引访问数据的类型慢100倍
Map使用make()创建,支持 := 这种简写方式
make([keyType]valueType, cap),cap表示容量,可省略
超出容量时会自动扩容,但尽量提供一个合理的初始值
使用len()获取元素个数
键值对不存在时自动添加,使用delete()删除某键值对
使用 for range 对map和slice进行迭代操作
map创建一:
func main() {
var m map[int]string
m = map[int]string{}
fmt.Println(m)
}
map创建二:
func main() {
var m map[int]string
m = make(map[int]string)
fmt.Println(m)
}
map创建三:
func main() {
m := make(map[int]string)
fmt.Println(m)
}
map简单操作:
func main() {
m := make(map[int]string)
m[1] = "OK"
a := m[1]
fmt.Println(a)
delete(m, 1) //从m中删除key为1的值
fmt.Println(m)
}
map复杂操作一:
func main() {
var m map[int]map[int]string
m = make(map[int]map[int]string)
m[1] = make(map[int]string)
m[1][1] = "ok"
a := m[1][1]
fmt.Println(a)
}
map复杂操作二:
func main() {
var m map[int]map[int]string
m = make(map[int]map[int]string)
a, ck := m[2][1] //通过ck输出flase取反来能m[2]初始化
if !ck {
m[2] = make(map[int]string)
}
m[2][1] = "GOOD"
a, ck = m[2][1]
fmt.Println(a, ck)
}
map复杂操作三:
for range 排序
func main() {
sm := make([]map[int]string, 5)
for i := range sm {
sm[i] = make(map[int]string, 1)
sm[i][1] = "ok"
fmt.Println(sm[i])
}
fmt.Println(sm)
}
map复杂操作四:
package main
import (
"fmt"
"sort"
)
func main() {
m := map[int]string{1: "a", 2: "b", 3: "c", 4: "d", 5: "e"}
s := make([]int, len(m)) //定义slice
i := 0 //i为计算器,定义在for的外部
for k, _ := range m { //遍历map的key ,map输出是无序的,需要排序
s[i] = k
i++
}
sort.Ints(s) //sort给Int类型排序
fmt.Println(s)
}
实例:将map[int]string 和key和value对调成map[string]int类型的
func main() {
m1 := map[int]string{1: "a", 2: "b", 3: "c", 4: "d", 5: "e"}
fmt.Println(m1)
m2 := make(map[string]int)
for k, v := range m1 {
m2[v] = k
}
fmt.Println(m2)
}
函数function
Go 函数 不支持 嵌套、重载和默认参数
但支持以下特性:
无需声明原型、不定长度变参、多返回值、命名返回值参数
匿名函数、闭包
定义函数使用关键字 func,且左大括号不能另起一行
函数也可以作为一种类型使用
package main
import (
"fmt"
)
func main() {
E(1, 2, 3)
}
func A(a int, b string, c int) (int, string, int) {
return a, b, c
}
func B(a int, b string) int {
return a
}
//并行参数
func C(a, b, c int) int {
return a
}
//并行返回值
func D() (a, b, c int) {
a, b, c = 1, 2, 3
return a, b, c
}
//不定长变参,a接收不定长参数后,变成slice,
//不定长变参,必须是参数列表的最后一个。
func E(a ...int) {
fmt.Println(a)
}
样例一、
func main() {
a, b := 1, 2
E(a, b)
fmt.Println(a, b)
}
func E(s ...int) {
s[0] = 3
s[1] = 1
fmt.Println(s)
}
样例二
func main() {
a :=1
E(a)
fmt.Println(a)
}
func E(a int) {
a = 2
fmt.Println(a)
}
样例三
func main() {
a := 1
E(&a)
fmt.Println(a)
}
func E(a *int) {
*a = 2
fmt.Println(*a)
}
样例四
func main() {
a := E
a()
}
func E() {
fmt.Println("func E")
}
匿名函数和闭包
func main() {
f := closure(10)
fmt.Println(f(9))
fmt.Println(f(10))
fmt.Println(f(11))
}
//closure 的返回值类型是一个匿名函数(匿名函数带int返回值)
func closure(x int) func(int) int {
//打印x的内存地址
fmt.Printf("%p\n", &x)
//return一个匿名函数
return func(y int) int {
//打印x的内存地址
fmt.Printf("%p\n", &x)
return x + y
}
}
defer
的执行方式类似其它语言中的析构函数,在函数体执行结束后
按照调用顺序的相反顺序逐个执行
即使函数发生严重错误也会执行
支持匿名函数的调用
常用于资源清理、文件关闭、解锁以及记录时间等操作
通过与匿名函数配合可在return之后修改函数计算结果
如果函数体内某个变量作为defer时匿名函数的参数,则在定义defer
时即已经获得了拷贝,否则则是引用某个变量的地址
Go 没有异常机制,但有 panic/recover 模式来处理错误
Panic 可以在任何地方引发,但recover只有在defer调用的函数中有效
func main() {
for i := 0; i < 3; i++ {
defer fmt.Println(i)
}
}
defer 在闭包中的使用
func main() {
for i := 0; i < 3; i++ {
defer func() {
fmt.Println(i)
}()
}
}
defer 中的匿名函数中的i只是引用,在for循环退出才执行。
defer在函数出现错误的时候也运行的例子
panic恐慌/recover恢复示例
func main() {
A()
B()
C()
}
func A() {
fmt.Println("Func A")
}
func B() {
defer func() {
//err初始化为recover函数,err不为空值
if err := recover(); err != nil {
fmt.Println("Recover in B")
}
}()
//panic 用来引发B函数的错误
panic("Panic in B")
}
func C() {
fmt.Println("Func C")
}
闭包课后作业
func main() {
var fs = [4]func(){}
for i := 0; i < 4; i++ {
defer fmt.Println("defer i = ", i)
defer func() {
fmt.Println("defer_closuer i = ", i)
}()
fs[i] = func() {
fmt.Println("closure i = ", i)
}
}
for _, f := range fs {
f()
}
}
struct类型
type person struct {
Name string
Age int
}
func main() {
a := person{
Name: "joe",
Age: 19,
}
fmt.Println(a)
}
type person struct {
Name string
Age int
}
func main() {
a := person{
Name: "joe",
Age: 19,
}
fmt.Println(a)
A(a)
fmt.Println(a)
}
func A(per person) {
per.Age = 13
fmt.Println("A", per)
}
通过指针
func main() {
a := person{
Name: "joe",
Age: 19,
}
fmt.Println(a)
//指针
A(&a)
fmt.Println(a)
}
func A(per *person) {
per.Age = 13
fmt.Println("A", per)
}
推荐在初始化结构的时候,对结构名前+ &(取地址符号)
func main() {
a := &person{
Name: "joe",
Age: 19,
}
a.Name = "ok"
fmt.Println(a)
A(a)
B(a)
fmt.Println(a)
}
func A(per *person) {
per.Age = 13
fmt.Println("A", per)
}
func B(per *person) {
per.Age = 15
fmt.Println("B", per)
}
匿名结构
package main
import (
"fmt"
)
func main() {
//匿名结构定义方法
a := &struct {
Name string
Age int
}{
Name: "jade",
Age: 19,
}
a.Name = "ok"
fmt.Println(a)
}
匿名结构嵌套
package main
import (
"fmt"
)
type person struct {
Name string
Age int
//匿名结构嵌套
contact struct {
Phone, City string
}
}
func main() {
a := person{Name: "jade", Age: 19}
a.contact.Phone = "13829977881"
a.contact.City = "beijing"
fmt.Println(a)
}
匿名字段
type person struct {
//匿名字段
string
int
}
func main() {
//匿名字段赋值
a := person{"jade", 19}
fmt.Println(a)
}
相同类型的比较
type person struct {
Name string
Age int
}
func main() {
a := person{"jade", 19}
b := person{"jade", 19}
fmt.Println(a == b)
}
Go没有继承,但是可以组合来实现
package main
import (
"fmt"
)
type human struct {
sex int
}
type teacher struct {
//嵌入结构
human
Name string
Age int
}
type student struct {
//嵌入结构
human
Name string
Age int
}
func main() {
a := teacher{Name: "jade", Age: 19, human: human{sex: 0}}
b := student{Name: "lili", Age: 16, human: human{sex: 1}}
a.Age = 23
//嵌入结构的字段可以直接赋值
a.sex = 1
//或者通过匿名字段的名称来赋值
b.human.sex = 2
fmt.Println(a, b)
}
结构组合,内外层同名字段的处理,示例
package main
import (
"fmt"
)
type A struct {
B
Name string
C
}
type B struct {
Name string
}
type C struct {
Name string
}
func main() {
a := A{
Name: "A",
B: B{Name: "B"},
C: C{Name: "C"},
}
fmt.Println(a.Name, a.B.Name, a.C.Name)
}