切片
-
数组
-
一组数
-
数组是值传递的
-
创建数组 [size]int
-
数组的大小是不可变的
-
二维数组,数组套数组
-
冒泡排序
-
-
切片
-
切片本身是不存在数据的
-
底层是指向了一个数组
-
如果我们存放的数据大于了切片的容量,在底层就会扩容 copy > 1024 1.25倍
-
使用数组来创建切片
package main import "fmt" // 切片本身是不保存数据的,只是指向了底层数组。 func main() { // 数组 [0,10) 数组的截取 [start,end] // 数组 arr := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} // 0xc000018230 // 通过数组来创建切片 [start,end) /* arr = 10个数 1, 0xc000000001 2, 0xc000000002 3, 0xc000000003 4, 0xc000000004 5, 0xc000000005 */ s1 := arr[:5] // 1-5 // 0xc000018230 {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} s2 := arr[3:8] // 4-8 // 0xc000018248 s3 := arr[5:] // 6-10 // 0xc000018258 s4 := arr[:] // 0-10 // 0xc000018230 fmt.Println(s1) fmt.Println(s2) fmt.Println(s3) fmt.Println(s4) // 查看容量和长度 fmt.Printf("s1 len:%d, cap:%d\n", len(s1), cap(s1)) // s1 len:5, cap:10 fmt.Printf("s2 len:%d, cap:%d\n", len(s2), cap(s2)) // s2 len:5, cap:7 fmt.Printf("s3 len:%d, cap:%d\n", len(s3), cap(s3)) // s3 len:5, cap:5 fmt.Printf("s4 len:%d, cap:%d\n", len(s4), cap(s4)) // s4 len:10, cap:10 // 内存地址 fmt.Printf("%p\n", s1) // 指向了原数组 fmt.Printf("%p\n", s2) // 截断了后,指向了最新截断的元素的下标开始 fmt.Printf("%p\n", s3) fmt.Printf("%p\n", &arr[5]) fmt.Printf("%p\n", s4) // 指向了原数组 fmt.Printf("%p\n", &arr) // 修改数组的内容, 切片也随之发生了变化 (切:切片不保存数据-->底层的数组 ) arr[2] = 100 fmt.Println(arr) // [1 2 100 4 5 6 7 8 9 10] fmt.Println(s1) // [1 2 100 4 5] fmt.Println(s2) // [4 5 6 7 8] fmt.Println(s3) // [6 7 8 9 10] fmt.Println(s4) // [1 2 100 4 5 6 7 8 9 10] fmt.Println("------------------------") // 修改切片的内容,发现数组也随之发生了变化。(本质:修改的都是底层的数组) s2[2] = 80 fmt.Println(arr) // [1 2 100 4 5 80 7 8 9 10] fmt.Println(s1) // [1 2 100 4 5] fmt.Println(s2) // [4 5 80 7 8] fmt.Println(s3) // [80 7 8 9 10] fmt.Println(s4) // [1 2 100 4 5 80 7 8 9 10] fmt.Println("------------------------") // 切片扩容 s1 = append(s1, 11, 12, 13, 14, 15, 16) fmt.Println(arr) // [1 2 100 4 5 80 7 8 9 10] // 如果在切片容量内加,会导致原来的数组数据发生修改。只有扩容之后,才会导致底层数组会产生拷贝。新的数组 fmt.Println(s1) // [1 2 100 4 5 11 12 13 14 15 16] fmt.Println(s2) // [4 5 80 7 8] fmt.Println(s3) // [80 7 8 9 10] // 查看内存地址,已经不是同一个了 fmt.Printf("%p\n", s1) // 0xc0000d2000 fmt.Printf("%p\n", &arr) // 0xc0000ac0f0 }
切片:引用类型
package main import "fmt" func main() { // 值类型,传递:copy arr1 := [4]int{1, 2, 3, 4} arr2 := arr1 fmt.Println(arr1, arr2) arr1[0] = 100 fmt.Println(arr1, arr2) // 切片:引用类型 s1 := []int{1, 2, 3, 4} s2 := s1 fmt.Println(s1, s2) s1[0] = 100 fmt.Println(s1, s2) // 通过内存分析发现,s1,s2指向了同一个地址。 fmt.Printf("%p\n", s1) fmt.Printf("%p\n", s2) }
深拷贝、浅拷贝
深拷贝:拷贝是数据的本身
-
值类型的数据,默认都是深拷贝,array、int、float、string、bool、struct....
浅拷贝:拷贝是数据的地址,会导致多个变量指向同一块内存。
-
引用类型的数据: slice、map
-
因为切片是引用类的数据,直接拷贝的是这个地址
切片怎么实现深拷贝 copy
package main import "fmt" // 切片实现深拷贝 func main() { // 将原来切片中的数据拷贝到新切片中 s1 := []int{1, 2, 3, 4} s2 := make([]int, 0) // len:0 cap:0 for i := 0; i < len(s1); i++ { s2 = append(s2, s1[i]) } fmt.Println(s1) fmt.Println(s2) s1[0] = 100 fmt.Println(s1) fmt.Println(s2) // copy s3 := []int{5, 6, 7} fmt.Println(s2) fmt.Println(s3) // 将s3中的元素拷贝到s2中 //copy(s2, s3) // 将s2中的元素拷贝到s3中 copy(s3, s2) fmt.Println(s2) fmt.Println(s3) }
函数中参数传递问题
按照数据的存储特点来分:
-
值类型的数据:操作的是数据本身、int 、string、bool、float64、array...
-
引用类型的数据:操作的是数据的地址 slice、map、chan....
值传递
引用传递
package main import "fmt" func main() { arr1 := [4]int{1, 2, 3, 4} fmt.Println("arr1:", arr1) update(arr1) fmt.Println("end arr1:", arr1) s1 := []int{1, 2, 3, 4} fmt.Println("s1:", s1) update2(s1) fmt.Println("end s1:", s1) } // 函数补充:在使用函数的时候,一定要特别注意参数问题,如果是值类型的,很多传递是无效的。 // 一些值传递的类型的参数,如果我们想通过函数来进行修改对应的值,这个时候就需要使用指针 // 指针变量 -> 指向原来变量的地址 // 数组是值类型的 func update(arr [4]int) { fmt.Println("--> arr:", arr) arr[0] = 100 fmt.Println("--> end arr:", arr) } // 切片是引用类型的 func update2(s []int) { fmt.Println("--> s:", s) s[0] = 100 fmt.Println("--> end s:", s) }
map
Map 是一种无序的键值对的集合。
-
无序 :map[key]
-
键值对:key - value /key - value key - value key - value
Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。
Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。
不过,Map 是无序的,我们无法决定它的返回顺序。
Map也是引用类型
map的定义
package main import "fmt" // map 集合,保存数据的一种结构 func main() { // 创建一个map,也是一个变量,数据类型是 map // map[key]value var map1 map[int]string // 只是声明了但是没有初始化,不能使用 nil if map1 == nil { fmt.Println("map1==nil") } // 更多的时候使用的是make方法创建 var map2 = make(map[string]string) // 创建了map fmt.Println(map1) fmt.Println(map2) // 在创建的时候,添加一些基础数据 // map[string]int nil // map[string]int {key:value,key:value,......} var map3 = map[string]int{"Go": 100, "Java": 10, "C": 60} fmt.Println(map3) // 关于map的类型,就如定义的一般 map[string]int // 类型主要是传参要确定 fmt.Printf("%T\n", map3) }
map的使用
-
创建并初始化map
-
map[key] = value 。将vaule赋值给对应的map的key
-
判断key是否存在,value, ok = map[key]
-
删除map中的元素, delete(map,key)
-
新增 map[key] = value
-
修改 map[key] = value ,如果存在这个key就是修改。
-
查看map的大小, len(map)
package main import "fmt" // 内存中的一个简单的数据库。key - value 。 增删改查的。 // map的使用 // 定义 var m1 map[key]value make(map[key]value) func main() { // 创建map var map1 map[int]string // nil,不能使用的,只是声明了,还没有初始化 // 给map1赋值 // 初始化map map1 = make(map[int]string) // 在map中如果你的key重复了,它就会覆盖这个key原来的值,一个key只能对应一个value map1[100] = "学相伴" map1[100] = "学相伴1111" map1[200] = "狂神说" // 获取数据 map[key] fmt.Println(map1) fmt.Println(map1[200]) fmt.Println(map1[1]) // 不存在,默认值 string "" // map中,没有index下标,有时候我们取值就需要判断这个key是否存在了 // map中的判断,ok-idiom 是否存在的 // value = map[key] , 隐藏的返回值 ok-idiom , 可选参数 value, ok := map1[1] // if ok { fmt.Println("map key 存在的,value:", value) } else { fmt.Println("map key 不存在的") } // 修改数据 map1[100] = "飞哥" fmt.Println(map1) // 如果数据存在,修改它,如果不存在,就会创建对应的 key-value map1[1] = "xxxxxxxxxxxxxxx" fmt.Println(map1) // map中的数据删除问题 delete delete(map1, 1) fmt.Println(map1) // map的大小 fmt.Println(len(map1)) }
map遍历
for k, v := range map1 , key-value,每次打印可能是无序的。
package main import "fmt" /* 遍历map - key、value 无序的,遍历map,可能每次的结果排序都不一致。 - "aaa" "aaaaa" - "bbb" "bbbbb" - "ccc" "ccccc" 1、map是无序的,每次打印出来的map可能都不一样,它不能通过index获取,只能通过key来获取 2、map的长度是不固定的,是引用类型的 3、len可以用于map查看map中数据的数量,但是cap无法使用 4、map的key 可以是 布尔类型,整数,浮点数,字符串 */ func main() { var map1 = map[string]int{"Go": 100, "Java": 99, "C": 80, "Python": 60} // 循环遍历,只能通过for range // 返回 key 和 value for k, v := range map1 { fmt.Println(k, v) } }
map结合slice使用
package main import "fmt" // map 结合 slice 来使用 /* 需求: 1、创建map来存储人的信息,name,age,sex,addr 2、每个map保存一个的信息 3、将这些map存入到切片中 4、打印这些数据 */ func main() { user1 := make(map[string]string) user1["name"] = "kuangshen" user1["age"] = "27" user1["sex"] = "男" user1["addr"] = "重庆" user2 := make(map[string]string) user2["name"] = "feige" user2["age"] = "30" user2["sex"] = "男" user2["addr"] = "长沙" user3 := map[string]string{"name": "小蓝", "age": "18", "sex": "男", "addr": "火星"} fmt.Println(user3) // 3个数据有了,存放到切片中,供我们使用 userDatas := make([]map[string]string, 0, 3) userDatas = append(userDatas, user1) userDatas = append(userDatas, user2) userDatas = append(userDatas, user3) fmt.Println(userDatas) // 0 map[string]string for _, user := range userDatas { //fmt.Println(i) fmt.Println(user["addr"]) } }
作业
1、通过map和slice 写一个简单的图书数据库,并可以通过书名或者书号来查询书的位置
books -book : num,name,addr,xxxx ,xx
2、了解函数传参中,引用类型和值类型的问题
-
值类型的数据,无法修改原来的数据的。 如果要修改-->1、指针 2/直接操作地址。