切片与map
切片Slice
Go语言切片是对数组的抽象
切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大
切片与数组相比,不需要设定长度,在[]中不用设定值,相对来说比较自由
从概念来说,slice像一个结构体,这个结构体包含了三个元素:
指针:指向数组中slice指定的开始位置
长度:即slice的长度
最大长度:也就是slice开始位置到数组的最后位置的长度
定义切片
package main
import "fmt"
func main() {
//数组的长度一旦定义就是不可以改变的
arr := [4]int{1, 2, 3, 4}
fmt.Println(arr)
//定义切片:长度就是可变的 []int
var s1 []int
fmt.Println(s1)
if s1 == nil {
fmt.Println("切片为空")
}
s2 := []int{1, 2, 3, 4, 5, 6, 7}
fmt.Println(s2)
fmt.Printf("%T,%T\n", s1, arr) //[]int,[4]int
fmt.Println(s2[0])
fmt.Println(s2[1])
for i := range s2 {
fmt.Print(s2[i], " ")
}
}
make函数创建切片
package main
import "fmt"
func main() {
s1 := make([]int, 5, 10)
fmt.Println(s1)
fmt.Println("长度为:", len(s1))
fmt.Println("容量为:", cap(s1))
s1[1] = 100
//s1[6] = 200//panic: runtime error: index out of range [6] with length 5
fmt.Println(s1)
}
切片扩容和遍历
package main
import "fmt"
func main() {
//append扩容
s1 := make([]int, 0, 5)
s1 = append(s1, 1, 2, 3)
fmt.Println(s1)
//如果切片中的数据超过了规定的容量,那么他就会自动扩容
s1 = append(s1, 4, 5, 6, 7, 8, 9)
fmt.Println(s1)
s2 := []int{1, 2, 3, 1, 23, 4}
s1 = append(s1, s2...)
fmt.Println(s1)
for i := 0; i < len(s1); i++ {
fmt.Print(s1[i], " ")
}
fmt.Println()
for _, v := range s1 {
fmt.Print(v, " ")
}
}
扩容的内存分析
package main
import "fmt"
func main() {
s1 := []int{1, 2, 3}
fmt.Println(s1)
fmt.Printf("len:%d,cap:%d\n", len(s1), cap(s1)) //len:3 cap:3
s1 = append(s1, 4, 5)
fmt.Println(s1)
fmt.Printf("len:%d,cap:%d\n", len(s1), cap(s1)) //len:5,cap:6
fmt.Printf("%p\n", s1)
s1 = append(s1, 6, 7, 8)
fmt.Println(s1)
fmt.Printf("len:%d,cap:%d\n", len(s1), cap(s1)) //len:8,cap:12
fmt.Printf("%p\n", s1)
s1 = append(s1, 9, 10)
fmt.Println(s1)
fmt.Printf("len:%d,cap:%d\n", len(s1), cap(s1)) //len:10,cap:12
fmt.Printf("%p\n", s1)
s1 = append(s1, 11, 12, 13)
fmt.Println(s1)
fmt.Printf("len:%d,cap:%d\n", len(s1), cap(s1)) //len:13,cap:24
fmt.Printf("%p\n", s1)
}
扩容的内存分析
- 每一个切片引用了一个底层数组
- 切片本身不存储任何数据,都是这个底层数组存储,所以修改切片也就是修改这个数组中的数据
- 当向切片中添加数据时,如果没有超过容量,直接添加,如果超过容量,自动扩容,成倍增加
package main
import "fmt"
func main() {
numbers := []int{1, 2, 3}
fmt.Printf("len=%d,cap=%d,slice=%v", len(numbers), cap(numbers), numbers)
//创建切片,numbers1 是之前切片的2倍容量
numbers1 := make([]int, len(numbers), (cap(numbers))*2)
fmt.Println()
//拷贝numbers的内容到number1
copy(numbers1, numbers)
fmt.Printf("len=%d,cap=%d,slice=%v", len(numbers1), cap(numbers1), numbers1)
}
- 切片一旦扩容,就是重新指向一个新的底层数组
在已有数组上创建切片
从已有数组上,直接创建切片,该切片的底层数组就是当前的数组,长度是从start到end切割到数据量,但是容器从start到数组的末尾
slice := arr[start:end]
package main
import "fmt"
func main() {
arr := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
// 0, 1,2,3, 4,5, 6,7,8, 9
fmt.Println("=============通过数组创建切片=============")
//arr[start包含:end不包含]
s1 := arr[:5] //1-5
s2 := arr[3:8] //4-8
s3 := arr[5:] //6-10
s4 := arr[:] //1-10
fmt.Println(s1)
fmt.Println(s2)
fmt.Println(s3)
fmt.Println(s4)
fmt.Printf("%p\n", &arr)
fmt.Printf("%p\n", s1)
fmt.Printf("len:%d,cap:%d\n", len(s1), cap(s1))
fmt.Printf("len:%d,cap:%d\n", len(s2), cap(s2))
fmt.Printf("len:%d,cap:%d\n", len(s3), cap(s3))
fmt.Printf("len:%d,cap:%d\n", len(s4), cap(s4))
arr[0] = 100
fmt.Println(arr)
fmt.Println(s1)
arr[1] = 200
fmt.Println(arr)
fmt.Println(s1)
s1 = append(s1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1)
fmt.Println(arr)
fmt.Println(s1)
s1[0] = 400
fmt.Println(arr)
fmt.Println(s1)
fmt.Printf("%p\n", &arr)
fmt.Printf("%p\n", s1)
}
切片:引用类型
package main
import "fmt"
func main() {
//数组,值类型
arr1 := [4]int{1, 2, 3, 4}
arr2 := arr1
fmt.Println(arr1, arr2)
arr2[0] = 100
fmt.Println(arr1, arr2)
fmt.Printf("arr1:%p\narr2:%p\n", &arr1, &arr2)
fmt.Println("========================")
//切片,引用类型
s1 := []int{1, 2, 3, 4}
s2 := s1
fmt.Println(s1, s2)
s2[0] = 100
fmt.Println(s1, s2)
fmt.Printf("s1:%p\ns2:%p", s1, s2)
}
深拷贝、浅拷贝
深拷贝:拷贝的是数据本身
- 值类型的数据。默认都是深拷贝:array,int,float,string,bool,sturct
浅拷贝:拷贝的是数据的地址,会导致多个变量指向同一块内存
- 引用类型的数据,默认都是浅拷贝:slice,map
- 因为切片是引用类型的数据,直接拷贝的是地址
实现切片深拷贝
package main
import "fmt"
func main() {
s1 := []int{1, 2, 3, 4, 5}
s2 := make([]int, 0, 0)
//实现切片深拷贝
for i := 0; i < len(s1); i++ {
s2 = append(s2, s1[i])
}
fmt.Println(s1)
fmt.Println(s2)
s2[0] = 100
fmt.Println(s1)
fmt.Println(s2)
fmt.Println("=============")
//copy函数实现深拷贝
s3 := []int{4, 5, 6}
fmt.Println(s3)
fmt.Println(s1)
copy(s1, s3)
fmt.Println(s3)
fmt.Println(s1)
}
集合Map
Map是一种无序的键值对的集合。Map最重要的一点是通过key来快速检索数据,key类似于索引,指向数据的值。
Map是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map是无序的,我们无法决定它的返回顺序。
Map也是引用类型
map[key]value
package main
import "fmt"
func main() {
//map 类型的定义:map[key]value
var map1 map[int]string // true nil 未创建
var map2 = make(map[string]string) // map[] 创建
var map3 = map[string]int{"Go": 100, "Java": 90, "C": 60}
fmt.Println(map1) //nil
fmt.Println(map2)
fmt.Println(map3)
fmt.Println(map1 == nil)
fmt.Println(map2 == nil)
for k, v := range map3 {
fmt.Println(k, v)
}
map2["yama"] = "zuo"
fmt.Println(map2)
}
map的使用
package main
import "fmt"
func main() {
//创建map
map1 := make(map[int]string)
//存储键值对,给键值对赋值
map1[1] = "yama"
map1[2] = "go"
map1[3] = "zhao"
map1[4] = "zuo"
fmt.Println(map1)
fmt.Println(map1[1]) //根据key,来获取value
fmt.Println(map1[5]) //key不存在,则获取默认的零值""
//可以通过ok-idiom来判断key value是否存在
value, ok := map1[3]
if ok {
fmt.Println("map key存在的,value,", value)
} else {
fmt.Println("map key不存在")
}
map1[1] = "我修改了数据"
fmt.Println(map1[1])
//map 删除 delete(map,key)
delete(map1, 1)
fmt.Println(map1)
fmt.Println(len(map1))
map1[10] = "aaa"
fmt.Println(len(map1))
}
map的遍历
⚠️⚠️
- map是无序的,每次打印出来的map都会不一样,它不能通过index获取,而必须通过key获取
- map的长度是不固定的,也就是和slice一样,也是一种引用类型
- 内置的len函数同样适用于map,返回map拥有的key的数量
- map的key可以是所有可比较的类型,如布尔型、整数型、浮点型、字符串…
package main
import "fmt"
func main() {
var map1 = map[string]int{"go": 100, "python": 99, "c": 98}
//每次打印出来的结果都不一样
for k, v := range map1 {
fmt.Println(k, v)
}
map2 := map1
map2["go"] = 0
fmt.Println(map1)
}
map结合slice
需求
- 创建map来存储人的信息:name,age,sex,addr
- 每个map存一个人的信息
- 将这些map存入到slice中
- 打印遍历输出
package main
import "fmt"
func main() {
user1 := make(map[string]string)
user1["name"] = "zuo"
user1["age"] = "18"
user1["sex"] = "男"
user1["addr"] = "深圳"
user2 := make(map[string]string)
user2["name"] = "zhao"
user2["age"] = "19"
user2["sex"] = "男"
user2["addr"] = "深圳"
user3 := map[string]string{"name": "jiang", "age": "20", "sex": "女", "addr": "重庆"}
fmt.Println(user1)
fmt.Println(user2)
fmt.Println(user3)
userDatas := make([]map[string]string, 0, 3)
userDatas = append(userDatas, user1)
userDatas = append(userDatas, user2)
userDatas = append(userDatas, user3)
fmt.Println(userDatas)
for _, v := range userDatas {
fmt.Printf("name:%s ", v["name"])
fmt.Printf("age:%s ", v["age"])
fmt.Printf("sex:%s ", v["sex"])
fmt.Printf("addr:%s\n", v["addr"])
}
}