这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天
前言
大家好呀,这是我参加青训营伴学笔记创作活动的第 2 天,如存在问题,烦请各位斧正!
切片
1、概述
1)简单地说,切片就是一种简化版的动态数组。动态数组的长度不固定。
2)切片高效操作的要点是要降低内存分配的次数,尽量保证append操作不会超出cap的容量,降低触发重分配次数和每次分配内存大小。
2、切片的定义
1)切片的结构定义:
reflect.SliceHeader:type SliceHeader struct {
Data uintptr // 指向底层数组的指针
Len int // 切片长度
Cap int // 切片最大长度,即阈值,
}
2)
len(s)和cap(s)分别返回切片的长度和容量3)当切片长度达到该阈值时才会对切片进行扩容,会自动扩容然后返回一个新的切片。(长度1024以前成倍扩容,1024以后1.25倍
3、声明切片
1)声明一个切片:
s := make([]string, 3) // 长度为3的string切片2)声明切片传入阈值:
s2 := make([]string, 0, 10) // 长度0,阈值103)可以 声明 一个 未指定大小的数组 来定义切片:
var name []type。此时它的值为nil,因为还没分配空间。4)从数组中切片,将数组从下标a到b的元素创建为一个新的切片:(a和b缺省都默认a为第一个元素、b为最后一个元素)
arr := [5]int{1, 2, 3, 4, 5} // 定义数组
var s6 []int // 定义切片
s6 = arr[1:4]
(这种方式下,并非复制了一个新的底层数组,切片指向的底层数组仍是赋值给切片的数组)
5)从字符串中切片,因为字符串底层是个byte数组,也可以切片:
(1)
s1 :=str1[0:5](2)
s := []byte(str)(3)将切片转回字符串:
str = string(s)
4、操作切片
1)直接操作某个索引的元素:
s[0]= "a"2)使用append方法为数组添加新的元素:
s= append(s, "a", "b"),并返回更新后的切片。3)还可以使用append追加切片,过程中需要解包。
4)切片的深拷贝
copy(a, b)(1)拷贝切片a到b,复制的长度以len小的为准。
(2)copy返回值为拷贝的元素个数。
映射
1、概述
1)它是一个无序1对1键值对。无序的:每次打印出来的map也都会不一样。
2)内置的len函数同样适用于map,返回map拥有的key的数量。
3)map的key可以是所有可比较的类型,如布尔型、整数型、浮点型、复杂型、字符串型。
2、声明一个map(可以声明时赋一些初值)
1)
ages := map[string]int{
"jack":10,
"tom":20
}
2)
ages := make(map[string]int) // 这样之后才能赋值3)如果一个map是nil,即只声明了,没有对应的一个make或创建的结果,此时map也可以查找,删除,获取元素个数,但是不能给零值map元素赋值。
3、操作map
1)访问元素:
fmt.Println(ages["jack"])当元素不存在时,返回的是该类型的默认值,若想确定是否存在,可以接收一个布尔类型的值
r, ok := map1["unknown"]2)删除元素:
delete(ages,"jack")2)map的增长可能会导致已有元素被重新散列到新的存储位置,因此无法通过&来获取地址。
3)可以使用range迭代map,但迭代是无序的,可以使用一些常用api根据键排序之后再迭代。
函数与结构体方法
1、函数的声明
1)
func add(a int, b int) int {
return a + b
}
2)还可以像主函数一样无返回值
func main() { ... }3)还可以有多个返回值,如图:
2、概述
1)Go语言中,函数是第一类对象,我们可以将函数保持到变量中。
2)函数主要有具名和匿名之分,具名函数就像普通函数一样声明,而匿名函数由一个不带函数名的函数声明和函数体组成。
var Add = func(a, b int) int {
return a+b
}
3、结构体方法
1)Go语言的方法是关联到类型的,这样可以在编译阶段完成方法的静态绑定。
2)如下方法,将一个类型参数写在方法名前,此处视此方法为File类型独有的方法,注意要使用*File声明 而不是File,可以避免值拷贝。
func (f *File) CloseFile() error {
// ...
}
3)函数不可以重名,而对于不同类型的结构体,方法可以重名。
接口
1、概述
1)它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口。
2)接口又称为动态数据类型,在进行接口使用的的时候:
会将接口对位置的动态类型改为所指向的类型,会将动态值改成所指向类型的结构体。
2、接口的使用
1)接口声明:
type name interface {
多个方法声明...
}
2)实现接口:结构体实现这个接口,只需要把接口声明的方法都实现了,无需在结构体上声明。(参数列表和返回值一样)
注意:实现接口时,结构体方法上的结构体类型是否使用*,即(f File)或(f *File)同样决定了是否为值传递。
3)使用接口:可以让接口类型作为形式参数,也可以作为变量的类型。使用时传入实现这个接口的结构体类型。
3、空接口
没有任何方法的接口就是空接口,实际上每个类型都实现了空接口,所以空接口类型可以接受任何类型的数据。
type phone interface{} //定义一个空接口
func showmpType(q interface{}) {
//空接口作为参数,传进来任意类型参数判断其类型与打印其值
fmt.Printf("type:%T,value:%v\n", q, q)
}
4、类型断言
1)如下,传入的是一个接口的实现,然后使用 "变量.(实际类型)"判定它是否为实际类型。
func judgeType1(q interface{}) {
temp, ok := q.(string)
}
2)该断言语句有两个返回值,
(1)如果断言成功第一个返回值为该变量对应的数据,否则返回该类型的空值。
(2)第二个参数是一个布尔值,如果断言成功则返回的是一个true,否则返回false。
3)可以使用switch语句判断类型:
switch i := q.(type) {
case string:
fmt.Println("这是一个字符串!", i)
default:
fmt.Println("未知类型", i)
}
5、接口的嵌套
接口可以进行嵌套实现,通过大接口包含小接口:
type animal interface {
peo
dog
}
type peo interface {}
type dog interface {}