golang中的切片
1. 切片中追加数据,如果没有扩容,内存地址不发生变化
1 2 3 4 5 6 7 8 9 10 11 12 13 | // 1. 切片中追加数据,如果不扩容的话,内存地址不发生变化 v1 := make([]int, 1, 3) v2 := append(v1, 55) fmt.Println(v1, v2) fmt.Printf( "%p, %p\n" , &v1[0], &v2[0]) v1[0] = 999 fmt.Println(v1, v2) fmt.Printf( "%p, %p\n" , &v1[0], &v2[0]) // 有一个切片,请往切片中追加一个数据 v3 := make([]int, 1, 3) v3 = append(v3, 999) fmt.Println(v3) |
2. 切片中追加数据,如果需要扩容的话,内存中就会拷贝一份出来,内存地址也就发生了变化
注意:扩容前和扩容后内存地址是不同的。
3. 切片中的常见操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | package main import ( "fmt" "reflect" ) func main() { // 1. 长度和容量 v1 := []int{11, 22, 33} fmt.Println(len(v1), cap(v1)) //2. 索引 s1 := []string{ "alex" , "李杰" , "老男孩" } fmt.Println(s1[0], s1[1], s1[2]) s2 := make([]int, 2, 5) fmt.Println(s2[0], s2[1]) s2[0] = 999 fmt.Println(s2) // 3. 切片,通过切片切出来的数据和原切片内部存储的数据地址相同 v2 := []int{11, 22, 33, 44, 55, 66} v3 := v2[1:3] fmt.Println(v3, reflect.TypeOf(v3)) fmt.Printf( "%p,%p\n" , &v2[1], &v3[0]) fmt.Printf( "%p,%p\n" , &v2[2], &v3[1]) // 4. 追加 v4 := []int{11, 22, 33} v5 := append(v4, 999, 888, 777) // 扩容了 fmt.Println(v4, v5, cap(v5)) v5 = append(v5, []int{66, 55}...) // append第二个参数可以放一个切片 fmt.Println(v5, cap(v5)) // 5. 删除 v6 := []int{11, 22, 33, 44, 55, 66} deleteIndex := 2 result := append(v6[:deleteIndex], v6[deleteIndex+1:]...) // [11 22 44 55 66 66] [11 22 44 55 66] fmt.Println(v6, result) fmt.Println(len(v6), cap(v6), len(result), cap(result)) // 使用切片时不太会使用删除。1. 效率低 2. 修改了原切片的值, 建议使用链表 // 删除方式2:不修改原来切片的值,重新声明一个切片 //ret2 := make([]int, 0, len(v6)-1) //ret2 = append(ret2, v6[:deleteIndex]...) //ret2 = append(ret2, v6[deleteIndex+1:]...) //fmt.Println(v6, ret2) //fmt.Printf("%p,%p\n", &v6[0], &ret2[0]) // 6. 插入 v7 := []int{11, 22, 33, 44, 55, 66} insertIndex := 3 // 在索引3的位置插入99 result1 := make([]int, 0, len(v7)+1) result1 = append(result1, v7[:insertIndex]...) result1 = append(result1, 99) result1 = append(result1, v7[insertIndex:]...) fmt.Println(v7) fmt.Println(result1) fmt.Printf( "%p,%p\n" , &v7[0], &result1[0]) // 注意:效率低下,建议使用链表 // 7. 循环 v8 := []int{11, 22, 33, 44, 55, 66} for i, v := range v8 { fmt.Println(i, v) } } |
4. 嵌套
1 2 3 4 5 6 7 8 9 | v1 := [][]int{{11, 222, 88, 99}, {33, 44}} fmt.Println(v1) v2 := [][2]int{{11, 22}, {33, 44}, {55, 66}} fmt.Println(v2) v1[0][3] = 1111 fmt.Println(v1) v2[1][1] = 4444 fmt.Println(v2) |
5. 变量赋值到底赋值数据吗?
(1)不可修改类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | // 整型,数据复制了一份 v1 := 1 v2 := v1 fmt.Printf( "%p, %p\n" , &v1, &v2) // 布尔,数据复制了一份 b1 := false b2 := b1 fmt.Printf( "%p, %p\n" , &b1, &b2) // 浮点型,数据复制了一份 f1 := 3.14 f2 := f1 fmt.Printf( "%p, %p\n" , &f1, &f2) // 字符串型,数据复制了一份 // 只是复制了字符串的长度和指针地址,而并没有复制字符串本身的数据 // 同时:字符串内部元素是不可被修改的 s1 := "武沛齐" s2 := s1 fmt.Printf( "%p, %p\n" , &s1, &s2) // 数组,数据复制了一份 v1 := [2]int{6, 9}<br> v2 := v1<br> fmt.Printf("%p, %p\n", &v1, &v2)<br> fmt.Println(&v1[0], &v2[0]) |
(2)可变类型
1 2 3 4 5 6 7 8 9 10 | // 切片,数组不会复制,复制的只是切片的内存地址 v1 := []int{6, 9} v2 := v1 fmt.Printf( "%p, %p\n" , &v1, &v2) fmt.Println(&v1[0], &v2[0]) // 0xc0000aa070 0xc0000aa070 // 如果扩容,那么内部存储数据的数组就会重新开辟区域 v1 = append(v1, 999) fmt.Println(v1, v2) fmt.Println(&v1[0], &v2[0]) // 0xc0000a8080 0xc0000aa070 |
总结:目前所学的所有数据类型中,在修改切片的内部元素时,会造成所有的赋值的变量同时修改(不扩容)
扩展:"引用类型和值类型",官方不建议这么说,如果这样理解的话:切片是引用类型,整型、布尔、浮点型、字符串、数组都是值类型。
6. 切片练习题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | package main import "fmt" func main() {<br><br> // 嵌套1 v1 := [][]int{make([]int, 2, 5), make([]int, 2, 5)} v2 := append(v1[0], 99) v2[0] = 69 fmt.Println(v1) // [[69 0], [0 0]] fmt.Println(v2) // [69 0 99] <em id="__mceDel"> // 嵌套2 //v1 := [][]int{make([]int, 2, 5), make([]int, 2, 5)} //v2 := append(v1[0][1:], 99) //v2[0] = 69 // //fmt.Println(v1) // [[0, 69], [0, 0]] //fmt.Println(v2) // [69 99] } </em> |
7. 切片和数组的区别?
(1)切片使用变量赋值的时候,两个切片变量使用的是同一块数据内存,而数组使用变量赋值的时候,两个数组变量使用的是不同内存
(2)数组是定长的,而切片是可变长的动态数组,而且切片定义时可以指定长度和容量
8. new和make的却别?
(1)new返回的是一个指针,而make返回的是一个对象
(2)new也帮助我们进行了初始化,整型初始化值是0,而make在它的内部也可以设置默认长度和默认容量。
9. 切片当做函数参数传递时,长度复制(各是各的),值是复制的底层数组的指针
也就是说,长度互不影响,值在不扩容的前提下可以互相影响
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | package main import ( "fmt" "time" ) func main() { s := make([]int, 0, 10) go addValue(s) // 使用协程来查看当此函数中对slice对底层数组修改时也会影响其它slice引用的底层数组 time.Sleep(time.Second) s = append(s, 100, 200) fmt.Println( "第二次打印:" , "第一个元素的值:" , s[0], "数组地址:" , &s[0], "切片长度:" , len(s), "切片容量:" , cap(s), "切片值" , s) time.Sleep(time.Second * 3) } func addValue(list []int) { list = append(list, 1, 2, 3, 4) fmt.Println( "第一次打印:" , "第一个元素的值:" , list[0], "数组地址:" , &list[0], "切片长度:" , len(list), "切片容量:" , cap(list), "切片值" , list) time.Sleep(2 * time.Second) fmt.Println( "第三次打印:" , "第一个元素的值:" , list[0], "数组地址:" , &list[0], "切片长度:" , len(list), "切片容量:" , cap(list), "切片值" , list) } /* 第一次打印: 第一个元素的值: 1 数组地址: 0xc00012e0f0 切片长度: 4 切片容量: 10 切片值 [1 2 3 4] 第二次打印: 第一个元素的值: 100 数组地址: 0xc00012e0f0 切片长度: 2 切片容量: 10 切片值 [100 200] 第三次打印: 第一个元素的值: 100 数组地址: 0xc00012e0f0 切片长度: 4 切片容量: 10 切片值 [100 200 3 4] */ |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)