切片指向了底层的一个数组
切片的长度等于切片元素的个数
切片的容量是底层数组从切片的第一个元素到最后一个元素的数量
//默认情况下 切片的长度 == 容量
s1 := []int{1, 2, 3}
fmt.Printf("当前切片的长度 %d 切片容量 %d", len(s1), cap(s1))
在Go中,数组一旦创建,大小就固定了无dui法改变数组的长度 因此有了切片
切片的底层是动态数组 可以动态的往数组中添加,删除元素
类似与 Java 中的集合
//默认情况下 切片的长度 == 容量
s1 := []int{1, 2, 3}
//指定切片的容量和长度大小
ss1 := make([]int, 3, 5)
fmt.Printf("当前切片的长度 %d 切片容量 %d", len(ss1), cap(ss1))
// 创建切片的方式三 :
//先创建数组
a1 := [...]int{1, 2, 3}
//将数组转换为切片
sa1 := a1[0:len(a1)]
fmt.Printf("当前切片的长度 %d 切片容量 %d", len(sa1), cap(sa1))
切片一次扩容为上一次 容量*2 当元素长度 >
//默认情况下 切片的长度 == 容量
s1 := []int{1, 2, 3}
fmt.Printf("当前切片的长度 %d 切片容量 %d", len(s1), cap(s1))
// 添加一个元素 切片容量将增加2倍 长度将增加一
s1 = append(s1, 4)
fmt.Printf("当前切片的长度 %d 切片容量 %d", len(s1), cap(s1))
1、当需要的容量超过原切片容量的两倍时,会使用需要的容量作为新容量。
2、当原切片长度小于1024时,新切片的容量会直接翻倍。而当原切片的容量大于等于1024时,会反复地增加25%,直到新容量超过所需要的容量。
上述都是不精确的 容量在计算完毕后 还要考虑到内存的高效率利用,进行内存对齐
// 情景一 : 如果需要的容量大于当前容量*2
s1 := []int{0}
fmt.Printf("原始切片的长度 %d 切片容量 %d \n", len(s1), cap(s1))
s1 = append(s1, 1, 2, 3)
fmt.Printf("新切片的长度 %d 切片容量 %d \n", len(s1), cap(s1))
//输出结果 原始切片的长度 1 切片容量 1
// 新切片的长度 4 切片容量 4
//情景二 : 如果需要的容量 小于 当前容量 * 2 且 当前容量 少于 1024
s1 := make([]int, 99, 100) // 原有容量大于 1024 则 扩容规则为 oldcap = oldcap *2 则结果应为 200
fmt.Printf("原始切片的长度 %d 切片容量 %d \n", len(s1), cap(s1))
s1 = append(s1, 1, 2, 3)
fmt.Printf("新切片的长度 %d 切片容量 %d \n", len(s1), cap(s1)) //实际结果为 200
//情景二 : 如果需要的容量 小于 当前容量 * 2 且 当前容量 少于 1024
s1 := make([]int, 1022, 1024) // 原有容量大于 1024 则 扩容规则为 oldcap = oldcap *1.5 则结果应为 1536
fmt.Printf("原始切片的长度 %d 切片容量 %d \n", len(s1), cap(s1))
s1 = append(s1, 1, 2, 3)
fmt.Printf("新切片的长度 %d 切片容量 %d \n", len(s1), cap(s1)) //实际结果为 1536
s1 := []int{1, 2, 3}
s2 := make([]int, 2, 2)
copy(s2, s1) // 参数一: 目的地 参数二 : 来源
fmt.Println(s2)
切片复制时 将根据需要复制元素的切片长度进行复制 如上述代码所示
则 s2 的打印结果为 1,2
在 Go 语言中 不存在 对指针的操作 与内存相关的有两个符号 需要记住
s1 := 10
fmt.Printf("s1 的内存地址为 %v", &s1)
mt.Printf("根据内存地址取值 %v", *(&s1))
两者都是用来申请内存地址的 但 还是有区别的
new 主要用来给基本数据类型申请内存地址 而 make 是用来给 slice, map , chan 申请内存地址的
//创建一个map
m := make(map[string]int)
//创建一个map
m := make(map[string]int,[容量大小])
m["张三"] = 19
m["李四"] = 20
m["王五"] = 21
//遍历:
for key, value := range m {
fmt.Printf("%v ---- %v \n", key, value)
}
//遍历key
for key, _ := range m {
fmt.Println(key)
}
//遍历value
for _, value := range m {
fmt.Println(value)
}
//根据key删除 value
delete(m,"张三")
约定俗成使用 ok 标识一个 key是否存在
//判断key是否存在
i, ok := m["张三1"]
if !ok {
fmt.Printf("%v 不存在当前key \n", m)
} else {
fmt.Printf("当前key对应的值为 %v \n", i)
}
函数时对代码的封装 把一段代码抽取出来, 给它起个名字 每次使用的时候只需要调用函数名即可 提高了代码的利用率
func mymethod(x int, y int) (ret int) {
return x + y
}
注意事项 :
形参列表如果连续多个参类型数一致时,只需要写最后一个参数的类型 ,其他参数的类型可以省略
如果 我们对返回值进行了命名(等同于声明一个局部变量), 可以直接写一个return
func mymethod1(x,y int)(sum int) {
sum = x + y
return
}
func mymethod1(x, y int) (sum int, del int) {
sum = x + y
del = x - y
return
}
如果一个函数我们只想使用一次,那么可以使用匿名函数
//匿名函数赋值给一个变量
mn1 := func(x, y int) (int) {
return x + y
}
fmt.Println(mn1(1, 9))
//全局的匿名函数 赋值给一个变量
var (
m = func(x, y int) int {
return x + y
}
)
str := "what do you want do what"
split := strings.Split(str, " ")
//遍历 字符切片 往 map 集合中 存
m1 := make(map[string]int)
for _, word := range split {
//判断字符串是否已经存在
_, ok := m1[word]
if !ok {
//不存在
m1[word] = 1
} else {
//已经存在
m1[word] = m1[word] + 1
}
}
//打印结果
fmt.Println(m1)