go语言切片

切片

在这里插入图片描述

切片是一种数据结构,这种数据结构便于使用和管理数据集合。切片是围绕动态数组的概念构建的,可以按需自动增长和缩小。切片的动态增长是通过内置函数 append 来实现的。这个函数可以快速且高效地增长切片。还可以通过对切片再次切片来缩小一个切片的大小。因为切片的底层内存也是在连续块中分配的,所以切片还能获得索引、迭代以及为垃圾回收优化的好处。

  1. 声明切片
  • 使用 var 关键字声明切片。
  • 指定切片的类型。
  1. 初始化切片
  • 可以在声明时初始化切片。
  • 也可以使用 make 函数创建切片。
  1. 访问切片元素
  • 使用索引访问切片中的元素。
  1. 切片的长度和容量
  • 使用内置函数 len 获取切片的长度。
  • 使用内置函数 cap 获取切片的容量。
  1. 切片的动态扩展
  • 使用 append 函数动态扩展切片。
[]int
[]interface{}

内部实现

在运行期间,Slice是由如下的SliceHeader结构体进行表示的

type SliceHeader struct {Data        uintptr    // 数组指针Len         int        // 长度Cap         int        // 容量
}

这 3 个字段分别是指向底层数组的指针、切片访问的元素的个数(即长度)和切片允许增长到的元素个数(即容量)

在这里插入图片描述

和C++的vector一样,在进行扩容时,一般会大于实际需要的长度,在实际使用中能有效的减少扩容的次数。

空切片和nil切片

空切片和nil切片并不等价,空切片和nil切片是不同的。空切片是一个长度为0,容量为0的切片,而nil切片是一个未初始化的切片,它指向一个不存在的内存区域。

创建一个nil切片非常简单,只需要声明一个切片变量,而不需要初始化。

// 定义一个nil切片
var slice []int
// 定义一个nil切片
slice := []int(nil)

在这里插入图片描述

创建一个空切片,需要使用make函数来创建,但是指定长度和容量为0

slice := make([]int, 0)

当使用make指定的切片长度为0时,那么底层其实会创建一个指针指向zerobase的切片,这时的Data指向 &zerobase

// base address for all 0-byte allocations
var zerobase uintptr

每次调用make来创建切片,都会调用到runtime.slice.go中的makeslice函数,我们来看下当cap和len为0时makeslice的处理逻辑。

//len = 0, cap = 0
func makeslice(et *_type, len, cap int) unsafe.Pointer {// mem = 0, overflow = falsemem, overflow := math.MulUintptr(et.Size_, uintptr(cap))...return mallocgc(mem, et, true)
}func MulUintptr(a, b uintptr) (uintptr, bool) {// a|b < 1<<(4*goarch.PtrSize) 满足// a == 0 满足if a|b < 1<<(4*goarch.PtrSize) || a == 0 {// 这里会返回a * b = 0, overflow = falsereturn a * b, false}...
}
// 这里调用时 size = 0
func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {if gcphase == _GCmarktermination {throw("mallocgc called with gcphase == _GCmarktermination")}// size == 0if size == 0 {return unsafe.Pointer(&zerobase)}...
}

.空切片Data 为 &zerobase

在这里插入图片描述

使用示例

切片的初始化

  • 通过下标方式获得数组或者切片的一部分
  • 使用字面量对切片进行初始化
  • 使用关键字make创建切片
// 使用空字符串初始化一个长度为100个元素的切片
slice := []string{99:""}
// 引用方式初始化切片
slice := arr[0:3]
// 切片字面量来声明一个长度为3的切片,容量为3的切片
slice := []int{1,2,3}
// 只指定长度,那么底层长度和容量相同
slie := make([]int, 10)
// 创建一个长度为10,容量为20的切片,需要确保长度<=容量,否则会报错
slice := make([]int, 10, 20)

使用for range迭代的是副本不是引用

// 创建一个整型切片
// 其长度和容量都是 4 个元素
slice := []int{10, 20, 30, 40}
// 迭代每一个元素,使用range迭代切片是副本
for index, value := range slice {fmt.Printf("Index: %d Value: %d\n", index, value)
}

在这里插入图片描述

slice := make([]int, 5, 20)
// 切片引用为前闭后开 [1,3)
sliceRef := slice[1:3]
// 切片长度为2,容量为 20 - 1,引用之后切片前端会从引用点开始,剩余元素保留
fmt.Println(len(sliceRef), cap(sliceRef))
// 修改引用切片的内容也会同步修改原切片
sliceRef[0] = 100
// slice[1] = 100
fmt.Println(slice[1])
// 当修改超过切片长度但是在容量范围内的元素时,会抛出panic
//slice[6] = 200
//函数 append 总是会增加新切片的长度,而容量有可能会改变,也可能不会改变,这取决于被操作的切片的可用容量。
slice = append(slice, 200)
//slice := source[low:high:max]
//source 是原始切片或数组。
//low 是切片的起始索引(包含)。
//high 是切片的结束索引(不包含)。
//max 是切片的最大容量位置(不包含)
slice := slice[2:4:10]// 如果容量设置大于已有容量,会出现运行时错误
source := [5]int{1, 2, 3, 4, 5}
slice := source[2:3:4]
//长度 (len(slice)):切片的长度是从 low 到 high 之间的元素数量,即 high - low。
//容量 (cap(slice)):切片的容量是从 low 到 max 之间的元素数量,即 max - low。
//对于 slice[i:j:k] 或 [2:3:4]
//长度: j – i 或 3 - 2 = 1
//容量: k – i 或 4 - 2 = 2
fmt.Println("Slice:", slice)         // 输出: Slice: [3]
fmt.Println("Length:", len(slice))   // 输出: Length: 1
fmt.Println("Capacity:", cap(slice)) // 输出: Capacity: 2// 设置长度和切片容量一致的好处
// 创建字符串切片
// 其长度和容量都是 5 个元素
source := []string{"Apple", "Orange", "Plum", "Banana", "Grape"}
// 对第三个元素做切片,并限制容量
// 其长度和容量都是 1 个元素
slice := source[2:3:3]
// 向 slice 追加新字符串,因为引用时容量和长度一致,只要调用append() 方法,就会自动增加容量,
// 后面再修改引用切片就不会影响原切片的内容了
slice = append(slice, "Kiwi")

切片的子切片

package mainimport "fmt"func main() {// 创建一个整数切片numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}// 创建子切片,切片的引用subSlice := numbers[2:5] // 子切片从索引 2 到 4(不包括 5)// 打印子切片fmt.Println(subSlice) // 输出: [3 4 5]
}

切片使用注意事项

  • 初始容量设置:合理设置初始容量,减少内存重新分配的次数。
  • 避免不必要的拷贝:尽量减少频繁的 append 操作。
  • 预分配足够的容量:如果可以预估最终大小,一次性分配足够的容量。
  • 避免多次 append 操作:收集所有要添加的元素,一次性 append
  • 检查切片容量:在 append 前检查容量,必要时手动重新分配内存。
  • 使用 copy 函数:在某些情况下,使用 copy 函数可以更高效地复制数据。

避免不必要的拷贝

每次 append 操作超过当前切片的容量时,Go 会分配一个新的更大的底层数组,并将原有数据复制到新的数组中。频繁的拷贝操作会影响性能。

package mainimport "fmt"func main() {// 创建一个初始容量为 2 的切片numbers := make([]int, 0, 2)// 动态扩展切片for i := 1; i <= 15; i++ {numbers = append(numbers, i)}// 打印切片fmt.Println(numbers) // 输出: [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]
}

预分配足够的容量

如果可以预估最终切片的大小,建议一次性分配足够的容量,以减少内存重新分配的次数。

package mainimport "fmt"func main() {// 创建一个初始容量为 15 的切片numbers := make([]int, 0, 15)// 动态扩展切片for i := 1; i <= 15; i++ {numbers = append(numbers, i)}// 打印切片fmt.Println(numbers) // 输出: [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]
}

避免多次 append 操作,如果需要多次 append 操作,可以考虑先收集所有要添加的元素,然后一次性 append

package mainimport "fmt"func main() {// 创建一个初始容量为 10 的切片numbers := make([]int, 0, 10)// 收集要添加的元素newElements := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}// 一次性 appendnumbers = append(numbers, newElements...)// 打印切片fmt.Println(numbers) // 输出: [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]
}

在进行 append 操作前,可以检查当前切片的容量,以决定是否需要重新分配内存。一旦发生内存重新分配,就会存在大规模的copy,因此一定要提前初始化足够的切片,避免冲突发生切片赋值的情况

package mainimport "fmt"func main() {// 创建一个初始容量为 10 的切片numbers := make([]int, 0, 10)// 动态扩展切片for i := 1; i <= 15; i++ {if cap(numbers) < len(numbers) + 1 {// 需要重新分配内存newCapacity := cap(numbers) * 2if newCapacity < len(numbers) + 1 {newCapacity = len(numbers) + 1}newSlice := make([]int, len(numbers), newCapacity)copy(newSlice, numbers)numbers = newSlice}numbers = append(numbers, i)}// 打印切片fmt.Println(numbers) // 输出: [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]
}

在某些情况下,使用 copy 函数可以更高效地复制数据。

package mainimport "fmt"func main() {// 创建一个初始容量为 10 的切片numbers := make([]int, 0, 10)// 收集要添加的元素newElements := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}// 一次性 appendnumbers = append(numbers, newElements...)// 使用 copy 函数newNumbers := make([]int, len(numbers))copy(newNumbers, numbers)// 打印切片fmt.Println(newNumbers) // 输出: [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2024年一级建造师考试成绩,即将公布!

一级建造师考试成绩一般在考试结束后3个月左右的时间公布&#xff01; 根据官方通知&#xff0c;重庆、江苏、青海、江西、云南、湖南、福建、北京、山西、黑龙江等地在今年一建报名通知里提到&#xff1a;2024年一级建造师考试成绩预计于2024年12月上旬公布。考生可在这个时间…

基于Matlab的图像去噪算法仿真

中值滤波的仿真 本节选用中值滤波法对含有高斯噪声和椒盐噪声的图像进行去噪&#xff0c;并用Matlab软件仿真。 &#xff08;1&#xff09;给图像加入均值为0&#xff0c;方差为0.02的高斯噪声&#xff0c;分别选择33模板、55模板和77模板进行去噪 Matlab部分代码&#xff1…

交通流量预测:基于交通流量数据建立模型

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

嵌入式QT学习第4天:Qt 信号与槽

Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长 本章思维导图如下&#xff1a; 不使用 Qt Designer 的方式进行开发&#xff0c;用代码绘界面&#xff0c;可以锻炼我们的布局能力&#xff0c;和代码逻辑能力&#x…

多线程+线程池

普通线程的创建 三种创建方式实例&#xff1a; 多线程本质上是毫无关系的&#xff0c;执行顺序是不可预知的&#xff0c;但是由于callable方式创建的对象有返回值所以主函数在执行的时候&#xff0c;需要等待返回值回来才能继续执行其他线程&#xff0c;所以在这种状态下是…

mac访达打开终端

选择文件夹打开 选中文件夹&#xff0c;然后右键即可&#xff1a; 在当前文件夹打开 在访达的当前文件夹长按option键 左下角出现当前文件夹路径 右键即可打开终端

【模型剪枝】YOLOv8 模型剪枝实战 | 稀疏化-剪枝-微调

文章目录 0. 前言1. 模型剪枝概念2. 模型剪枝实操2.1 稀疏化训练2.2 模型剪枝2.3 模型微调总结0. 前言 无奈之下,我还是写了【模型剪枝】教程🤦‍♂️。回想当年,在写《YOLOv5/v7进阶实战专栏》 时,我经历了许多挫折,才最终完成了【模型剪枝】和【模型蒸馏】的内容。当时…

Django 路由层

1. 路由基础概念 URLconf (URL 配置)&#xff1a;Django 的路由系统是基于 urls.py 文件定义的。路径匹配&#xff1a;通过模式匹配 URL&#xff0c;并将请求传递给对应的视图处理函数。命名路由&#xff1a;每个路由可以定义一个名称&#xff0c;用于反向解析。 2. 基本路由配…

单点登录原理

允许跨域–>单点登录。 例如https://www.jd.com/ 同一个浏览器下&#xff1a;通过登录页面产生的cookie里的一个随机字符串的标识&#xff0c;在其他子域名下访问共享cookie获取标识进行单点登录&#xff0c;如果没有该标识则返回登录页进行登录。 在hosts文件下面做的域名…

保持角色一致性!flux新模型redux用法(含模型与工作流)

​ 目录 redux模型是什么&#xff0c;能干啥&#xff1f; 用到的工具有哪些&#xff1f; 工具和模型文件在哪里下载&#xff1f; 整合包&#xff1a; 下载后需要分别放到指定目录&#xff1a; redux模型怎么用&#xff1f; 加载工作流 上传图片和输入提示词 生成结果…

FastAPI 跨域访问cors设置

问题发现 前端vue3写了个页面&#xff0c;调用后台一个服务&#xff0c;出现了跨域访问错误&#xff0c;截图如下&#xff1a; 示例代码如下&#xff1a; from typing import Unionfrom fastapi import FastAPI from pydantic import BaseModel import randomapp FastAPI()…

Admin.NET框架使用宝塔面板部署步骤

文章目录 Admin.NET框架使用宝塔面板部署步骤&#x1f381;框架介绍部署步骤1.Centos7 部署宝塔面板2.部署Admin.NET后端3.部署前端Web4.访问前端页面 Admin.NET框架使用宝塔面板部署步骤 &#x1f381;框架介绍 Admin.NET 是基于 .NET6 (Furion/SqlSugar) 实现的通用权限开发…

音视频流媒体直播/点播系统EasyDSS互联网视频云平台介绍

随着互联网技术的飞速发展&#xff0c;音视频流媒体直播已成为现代社会信息传递与娱乐消费的重要组成部分。在这样的背景下&#xff0c;EasyDSS互联网视频云平台应运而生&#xff0c;它以高效、稳定、便捷的特性&#xff0c;为音视频流媒体直播领域带来了全新的解决方案。 1、产…

51单片机快速入门之中断的应用 2024/11/23 串口中断

51单片机快速入门之中断的应用 基本函数: void T0(void) interrupt 1 using 1 { 这里放入中断后需要做的操作 } void T0(void)&#xff1a; 这是一个函数声明&#xff0c;表明函数 T0 不接受任何参数&#xff0c;并且不返回任何值。 interrupt 1&#xff1a; 这是关键字和参…

某航客服部满意度调查及管理改进项目纪实

某航客服部满意度调查及管理改进项目纪实 ——采用信息化的调查工具&#xff0c;调研并提高员工积极性 【客户行业】航空航天 【问题类型】企业管理问题诊断 【客户背景】 某公司是某大型航空公司旗下的客户服务中心&#xff08;以下简称为客服部&#xff09;&#xff0c;…

[巅峰极客 2021]签到

[巅峰极客 2021]签到 给了我们好多表情&#xff0c;真的是一脸懵逼 注意给我们的关键词 GAME 现在还不知道是什么意思我们去试着解开一下 用这个emoji表情解密器&#xff0c;这里我找了好久才找到一个 emoji-aes 这里的Key值就是GAME 运行后出现flag NSSCTF{10ve_4nd_Peace…

docker-compose 升级

官方下载地址&#xff1a; https://github.com/docker/compose/releases 下载完放到kali root目录下 # mv docker-compose-Linux-x86_64 /usr/local/bin/docker-compose # chmod x /usr/local/bin/docker-compose # docker-compose --version

玄机应急:linux入侵排查webshell查杀日志分析

目录 第一章linux:入侵排查 1.web目录存在木马&#xff0c;请找到木马的密码提交 2.服务器疑似存在不死马&#xff0c;请找到不死马的密码提交 3.不死马是通过哪个文件生成的&#xff0c;请提交文件名 4.黑客留下了木马文件&#xff0c;请找出黑客的服务器ip提交 5.黑客留…

linux(centos) 环境部署,安装JDK,docker(mysql, redis,nginx,minio,nacos)

目录 1.安装JDK (非docker)1.1 将文件放在目录下&#xff1a; /usr/local/jdk1.2 解压至当前目录1.3 配置环境变量 2.安装docker2.1 验证centos内核2.2 安装软件工具包2.3 设置yum源2.4 查看仓库中所有docker版本&#xff0c;按需选择安装2.5 安装docker2.6 启动docker 并 开机…

内核模块里获取当前进程和父进程的cmdline的方法及注意事项,涉及父子进程管理,和rcu的初步介绍

一、背景 在编写内核态系统监控代码时&#xff0c;有时候为了调试的便捷性&#xff0c;不仅要拿到异常事件有关的线程id&#xff0c;进程id和父进程id&#xff0c;还需要拿到当前进程和父进程的comm和cmdline。主要有几下几个原因&#xff1a; 1&#xff09;单纯的pid或者tgi…