5.go切片和map

server/2025/3/30 22:25:57/

切片的概念

数组和切片相比较切片的长度是不固定的,可以追加元素,在追加时可能会使切片的容量增大,所以可以将切片理解成 "动态数组",但是,它不是数组,而是构建在数组基础上的更高级的数据结构。

切片和数组的对比

  • 数组在声明时需要指定长度,切片不需要。

  • 数组的长度是数组类型的一部分,因此 [5]int 和 [10]int 是不同的类型,不能互相赋值或比较。而切片只需要考虑元素类型。

  • 数组是值类型,当数组作为参数传递时,会被复制一份,因此对数组的修改不会影响原数组。而切片是引用类型,当切片作为参数传递时,只会传递一个指向底层数组的指针,因此对切片的修改会影响原切片。

  • 数组的长度是固定的,在内存中占用连续的空间。而切片的长度是可变的,在内存中是一个结构体,其中包含指向底层数组的指针、长度和容量三个字段。

切片声明

  • 使用var声明切片

语法:var 切片名称[] 数据类型

var arr []int //此时变量arr 是切片的零值 [],和nil比较,是相等的。此时len是0,cap是0

  • 使用自动推导类型声明切片

语法:切片名称 := []类型{}

arr := []int{} //此时变量是指向一个长度为0的数组,和nil不等,此时len是0,cap是0

arr := []int{1,2,3} //此时变量是指向一个长度为3的数组,此时len是3,cap是3

  • 使用make()函数生成切片

语法:make([]类型, 长度, 容量)

长度是已初始化空间

容量是已经开辟空间,包括已经初始化的空间和空闲空间。

arr := make([]int, 3, 5)

fmt.Printf("长度:%d\t容量:%d", len(arr), cap(arr))

slice内存模型

例子:

a1 := make([]int, 1, 3) //此时a1被分配了长度是3,【a1[0,x,x]】

a2 := append(a1, 1, 2) //此时append 两个,底层数组可以容纳,【a2[0,1,2]】

a3 := append(a1, -1) //此时在a1的基础上[0,x,x],添加一个元素,底层数组可以容纳【a3[0,-1]】

//此时因为使用的是同一个数组,影响了a2,此时【a2[0,-1,2]】

fmt.Println(a1, a2, a3)

向切片中添加元素

arr := make([]int, 3, 5)

arr[0] = 100 //添加100

arr[1] = 200 // 添加200

//panic: runtime error: index out of range [3] with length 3

//arr[3] = 200 //错误,不能大于申请的长度

   //append可以在申请长度后边新增元素,如果不大于申请容量,直接放在在源底层数组上,

  //如果大于申请容量,会重新分底层配数组,把原来的数据拷贝到新数组上,然后在后边加上新元素

fmt.Println(arr)

append()函数切片添加元素

arr = append(arr, 1) // 追加1个元素

fmt.Printf("arr元素%v,arr长度%d,arr容量%d,arr地址%p\n", arr, len(arr), cap(arr), &arr[0])

arr = append(arr, 2, 3) // 最加多个元素

fmt.Printf("arr元素%v,arr长度%d,arr容量%d,arr地址%p\n", arr, len(arr), cap(arr), &arr[0])

arr = append(arr, []int{5, 6, 7}...) // 追加一个切片

fmt.Printf("arr元素%v,arr长度%d,arr容量%d,arr地址%p\n", arr, len(arr), cap(arr), &arr[0])

注意:在使用 append() 函数为切片动态添加元素时,如果空间不足以容纳足够多的元素,切片就会进行“扩容”,此时新切片的长度会发生改变,会导致内存的重新分配,而且已有元素全部被复制 1 次到新分配的内存地址上。

... 运算符将切片展开为多个独立的参数

var arr = []int{1, 2, 3}

fmt.Printf("arr元素%v,arr长度%d,arr容量%d,arr[0]地址%p\n", arr, len(arr), cap(arr), &arr[0])

arr = append([]int{666}, arr...) // 定义一个新切片,并且把原来切片展开,添加到新切片里

fmt.Printf("arr元素%v,arr长度%d,arr容量%d,arr[0]地址%p\n", arr, len(arr), cap(arr), &arr[0])

内存分析

//此时,内存进行了三次分配,首先定义的a,会分配一个数量是3的底层数组,

// 定义[]int{666},时候,会分配一个数量是1的底层数组,

//往[]int{666} 添加切片时候,空间不够,会在开辟一块底层是4的数组,

//然后 拷贝[]int{666}到新数组,在拷贝a切片内容到新数组,最后把a切片底层指向数组的指针指向新分配的数组

copy()函数复制切片

Go语言的标准库提供了一个非常方便的copy函数,它可以用来复制切片(slice)或者数组(array)中的内容。

copy( destSlice, srcSlice)

  • srcSlice:数据来源切片

  • destSlice:复制的目标

在使用 copy 函数时,需要确保源切片和目标切片的长度相同

如果长度不同,copy 函数只会复制较短切片的长度。这可能导致数据丢失或意外的行为。

src := []int{1, 2, 3, 4, 5}

dst := make([]int, 3)

n := copy(dst, src) // n 将等于 3,而不是 5

遍历切片

arr := make([]int, 3, 5)

arr = append(arr, 200, 300)

// // 传统for循环

// for i := 0; i < len(arr); i++ {

// fmt.Println(arr[i])

// }

// for range遍历

for _, v := range arr {

fmt.Println(v)

}

子切片

  子切片是指在Go语言中,从一个已有的切片中创建出的一个新的切片。子切片与原始切片共享同一个底层数组,但拥有自己的长度和容量。因为子切片共享原切片的底层数组,因此对数据的更改会影响彼此。

  子切片写法

  切片[startIndex:endIndex]

  startIndex 是子切片的起始索引,endIndex 是子切片的结束索引(但不包括该索引处的元素)。

  如果省略 startIndex(切片[startIndex:]),则从切片的开始处截取;

  如果省略 endIndex(切片[:endIndex]),则截取到切片的末尾。

  如果都省略(切片[:]),则截取原切片全部。

  例子:

  s := []int{1, 2, 3, 4, 5}

  sub := s[1:4] // 创建从索引1到索引3的子切片,

  fmt.Println(sub) // 输出:[2 3 4]

  sub[0] = 20 // 修改sub的第一个元素

  fmt.Println(s) // 输出:[1 20 3 4 5],s也被修改

sub = append(sub, 300)

fmt.Println(s) // 输出:[1 20 3 4 300],底层分配的数组,后边还有未使用

  //,所以不会扩容,会在原来底层数组上做修改

map

在Go语言中,map 类型是一个内置的引用类型,它存储键值对(key-value pairs)。map 是由一个键类型和一个值类型构成的,其中键的类型必须是可比较的(例如,整型、字符串、结构体等,但不能是切片或函数等不可比较的类型)。

map 零值是 nil 。不能直接使用,需要make创建 或者 字面量赋值

var m map[string]int m["apple"] = 1 //错误:panic: assignment to entry in nil map

创建 map

你可以使用 make 函数或者使用字面量语法来创建一个 map

  • 使用 make 函数:

  • 使用字面量语法:

m := map[string]int{"apple":  5,"pear":   6,"grape":  8,
}

访问 map 中的元素

你可以通过键来访问 map 中的元素。如果键存在,它将返回对应的值;如果键不存在,它将返回零值(对于值类型)或者一个明确的错误。

value, ok := m["apple"] // value 为 5, ok 为 true value, ok = m["banana"] // value 为 0 (int 的零值), ok 为 false

修改 map 中的元素

你可以通过键来修改 map 中的元素。如果键存在,它的值将被更新;如果键不存在,一个新的键值对将被添加到 map 中。

m["apple"] = 10 // 将 "apple" 的值改为 10 m["banana"] = 3 // 添加一个新的键值对 "banana": 3

删除 map 中的元素

你可以使用 delete 函数来删除 map 中的元素。

delete(m, "apple") // 删除键为 "apple" 的元素

遍历 map

你可以使用 range 循环来遍历 map 中的所有键值对。在每次迭代中,range 会返回两个值:键和对应的值。

for key, value := range m { fmt.Println(key, value) }

map 的并发访问和修改

从Go 1.9开始,map 是并发安全的,你可以在多个goroutine中安全地读写同一个map。但是,如果你需要频繁地修改同一个map,最好还是限制对它的并发访问,以避免不必要的性能开销。如果你确实需要频繁地并发修改,可以考虑使用 sync.Map,它是专门为并发环境设计的。

注意事项

  • 零值:当你声明了一个 map 变量但没有初始化(即没有用 make 或字面量语法),它的零值是 nil。尝试在 nilmap 上进行读写操作将会导致运行时恐慌(panic)。因此,在使用前应确保 map 被正确初始化。

  • 并发安全:虽然Go的map在1.9版本后是并发安全的,但在高并发的场景下仍可能遇到性能瓶颈或需要更精细的控制,这时可以考虑使用 sync.Map

  • 性能考虑:虽然Go的map提供了很好的性能和灵活性,但在某些特定情况下(如频繁的删除操作),可能需要考虑其性能影响或选择其他数据结构(如平衡树)。

文章来源:https://blog.csdn.net/chxii/article/details/146490586
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ppmy.cn/server/179539.html

相关文章

C++20 中的std::c8rtomb和 std::mbrtoc8

文章目录 1. 引言2. std::c8rtomb 函数详解3. std::mbrtoc8 函数详解4. 使用示例5. 注意事项6. 总结 1. 引言 C20 标准引入了对 UTF-8 编码的更好支持&#xff0c;其中包括两个重要的函数&#xff1a;std::c8rtomb 和 std::mbrtoc8。这两个函数分别用于将 UTF-8 编码的字符转换…

深入理解垃圾收集算法:从分代理论到经典回收策略

垃圾收集&#xff08;Garbage Collection, GC&#xff09;是现代虚拟机自动内存管理的核心机制。它不仅能自动回收不再使用的对象&#xff0c;还能极大减轻开发者在内存管理上的负担。本文将详细讲解垃圾收集算法的基本思想、分代收集理论以及几种经典的垃圾收集算法。 注&…

Ceph集群2025(Squid版)导出高可用NFS集群(上集)

#创建一个CephFS 文件系统 ceph fs volume create cephfs02#创建子卷 ceph fs subvolumegroup create cephfs02 myfsg2#查看子卷 ceph fs subvolumegroup ls cephfs02[{"name": "myfsg2"} ]创建 NFS Ganesha 集群 #例子 $ ceph nfs cluster create <c…

【BFS染色问题】P1162填涂颜色例题+核心逻辑

文章目录 【算法思路】【代码示例】 BFS处理染色问题的核心逻辑 【算法思路】 要判断一个数字 0 是否在闭合圈内&#xff0c;可以换个角度思考。不在闭合圈内的 0 是可以从方阵的边界出发&#xff0c;通过上下左右移动&#xff0c;只经过其他 0 到达的。 思路①.我们可以从方…

用Deepseek写扫雷uniapp小游戏

扫雷作为Windows系统自带的经典小游戏&#xff0c;承载了许多人的童年回忆。本文将详细介绍如何使用Uniapp框架从零开始实现一个完整的扫雷游戏&#xff0c;包含核心算法、交互设计和状态管理。无论你是Uniapp初学者还是有一定经验的开发者&#xff0c;都能从本文中获得启发。 …

[思路提供]Mysql主从复制时的网络延迟很高,如何调整MySQL复制参数

在 MySQL 主从复制过程中&#xff0c;如果网络延迟很高&#xff0c;可以通过调整以下复制参数来优化数据同步&#xff1a; 增加复制并行度&#xff1a; slave_parallel_workers&#xff1a;从 MySQL 5.6 开始支持多线程复制&#xff0c;可将该值设置为大于 0 的值&#xff0c;根…

练习题:105

目录 Python题目 题目 题目分析 需求理解 关键知识点 实现思路分析 代码实现 代码解释 导入 random 模块&#xff1a; 定义列表&#xff1a; 随机选择元素&#xff1a; 打印结果&#xff1a; 运行思路 结束语 Python题目 题目 从一个列表中随机选择一个元素。 …

气象可视化卫星云图的方式:方法与架构详解

气象卫星云图是气象预报和气候研究的重要数据来源。通过可视化技术,我们可以将卫星云图数据转化为直观的图像或动画,帮助用户更好地理解气象变化。本文将详细介绍卫星云图可视化的方法、架构和代码实现。 一、卫星云图可视化方法 1. 数据获取与预处理 卫星云图数据通常来源…