当前位置: 首页 > news >正文

go语言Array 与 Slice

有的语言会把数组用作常用的基本的数据结构,比如 JavaScript,而 Golang 中的数组(Array),更倾向定位于一种底层的数据结构,记录的是一段连续的内存空间数据。但是在 Go 语言中平时直接用数组的时候不多,大多数场景下我们都会直接选用更加灵活的切片(Slice)

1 数组

声明与初始化

# 声明
var arr [5]int
var buffer [256]byte# 初始化方式有两种,一种是显示声明长度,另一种是[...]T推断长度,会经过编译器推导,得到数组长度
arr1 := [3]int{0,1,2}
arr2 := [...]string{"Joey","Sophie"}

Go 为不同类型不同结构的初始化方式进行了优化(不止是数组的初始化这一点上,其它一些代码同样如此),对于优化过程,可以简单概括为下面的话:

  • 如果数组中元素的个数小于或者等于 4 个,那么所有的变量会直接在栈上初始化;
  • 如果数组元素大于 4 个,变量就会在静态存储区初始化然后拷贝到栈上,这些转换后的代码才会继续进入中间代码生成和机器码生成两个阶段,最后生成可以执行的二进制文件。

2 slice

区别 Slice 与 Array 的首要关键是记住下面几点:

  • Slice 不是 Array,它描述一个 Array
  • Slice 的本质是一个 Struct这是他长度可变的根本原因

Go 源码中找到 sliceHeader 的定义:

type sliceHeader struct {Data unsafe.Pointer // 指向的数组Len  int            // 长度,即 Slice 截取 Data 的长度Cap  int            // 容量,即 Data 的大小,显然不会小于 Len
}

Slice 的声明方式比较多,我们可以直接构建一个空 Slice 而不需要指定长度,我们也可以直接基于 Array 本身构建一个 Slice,亦可以基于 Slice 构建新的 Slice

var sli0 = make([]int) // make([]T, Len, Cap)
var sli1 = arr1[5:10]
var sli2 = sli1[2:]

sli1 在 arr1 的左闭右开索引区间 [5, 10) 上构建了切片,而 sli2 又在 sli1 的基础上构建了 [2, 5) 的切片,这里值得记住的一点是,切片结构体里保存的是底层数组的指针(引用),因此他们指向的是同一块底层数组

2.1 函数传递 Slice

切片作为函数参数直接传递时就是个普通的值传递,但是 Slice 这个值很特殊,他里面存有数组的指针,又包含了 Slice 的 Len 和数组的 Cap,即又包含指针又包含普通值,因此:

  • 直接传递 Slice 进函数时,传递的是 Slice 的 copy;
  • 对 Slice 的元素进行修改操作,会通过指针直接修改数组,因此是可以实现的;
  • 对 Slice 的长度修改,修改的是 copy 对象的 Len 字段,因此原 Slice 是长度是不会变的;
  • 想要在函数内修改 Slice 的长度,最好的方式是传递 Slice 的指针;

2.2 容量与 append

append 返回的是一个新的 slice,直接 append 而不赋值给原 slice 的话,原 slice 长度是不会改变的

names = append(names, "Joey")
# 移除逻辑
ages = append(ages[:5], ages[6:])

关于容量需要记住的就是:当向 Slice 追加元素导致 Len大于 Cap 时,会触发扩容机制,创建一个Cap大于原数组的新数组(首元素地址不一致),并将值拷贝进新数组,之后再改变Slice元素值时改变的是新创建的数组(切断与原数组的引用关系)。是的,当触发扩容机制后,新的 Slice 底层数组已经不再是之前的数组了,对于 Slice 元素的修改都是基于新的底层数组进行。
因此我们如果真的关注性能这一块儿的话,一定要想办法避免频繁的触发扩容机制,比如当我们明确地知道 Slice 容量上限的时候,在声明时就应该通过 make([]T, Len, Cap) 给出明确的 cap 值

2.3 slice扩容

go1.17 及以下版本扩容机制如下:

代码的扩容策略可以简述为以下三个规则:
1.当期望容量 > 两倍的旧容量时,直接使用期望容量作为新切片的容量
2.如果旧容量 < 1024(注意这里单位是元素个数),那么直接翻倍旧容量
3.如果旧容量 > 1024,那么会进入一个循环,每次增加25%直到大于期望容量
可以看到,原来的go对于切片扩容后的容量判断有一个明显的magic number:1024,在1024之前,增长的系数是2,而1024之后则变为1.25。

关于为什么会这么设计,社区的相关讨论1给出了几点理由:1.如果只选择翻倍的扩容策略,那么对于较大的切片来说,现有的方法可以更好的节省内存。2.如果只选择每次系数为1.25的扩容策略,那么对于较小的切片来说扩容会很低效。3.之所以选择一个小于2的系数,在扩容时被释放的内存块会在下一次扩容时更容易被重新利用

func growslice(et *_type, old slice, cap int) slice {...newcap := old.capdoublecap := newcap + newcapif cap > doublecap {newcap = cap} else {if old.cap < 1024 {newcap = doublecap} else {// Check 0 < newcap to detect overflow// and prevent an infinite loop.for 0 < newcap && newcap < cap {newcap += newcap / 4}// Set newcap to the requested cap when// the newcap calculation overflowed.if newcap <= 0 {newcap = cap}}}...
}

go1.18 及以上版本扩容机制如下
在这里插入图片描述

//1.18
newcap := old.cap
doublecap := newcap + newcap
if cap > doublecap {newcap = cap
} else {const threshold = 256if old.cap < threshold {newcap = doublecap} else {// Check 0 < newcap to detect overflow// and prevent an infinite loop.for 0 < newcap && newcap < cap {// Transition from growing 2x for small slices// to growing 1.25x for large slices. This formula// gives a smooth-ish transition between the two.newcap += (newcap + 3*threshold) / 4}// Set newcap to the requested cap when// the newcap calculation overflowed.if newcap <= 0 {newcap = cap}}
}

在这里插入图片描述
在1.18中,优化了切片扩容的策略2,让底层数组大小的增长更加平滑:通过减小阈值并固定增加一个常数,使得优化后的扩容的系数在阈值前后不再会出现从2到1.25的突变,该commit作者给出了几种原始容量下对应的“扩容系数”:
在这里插入图片描述

内存对齐,进一步调整newcaps
slice的扩容还与数据类型有关,当数据类型size为1字节,8字节,或者2的倍数时,会根据内存大小进行向上取整,之后返回新的扩容大小。
这是由于Go语言的内存管理模块返回给你需要的内存块,通常这些内存块都是预先申请好,并且被分为常用的规格,比如8,16, 32, 48, 64等。

相关文章:

go语言Array 与 Slice

有的语言会把数组用作常用的基本的数据结构&#xff0c;比如 JavaScript&#xff0c;而 Golang 中的数组(Array)&#xff0c;更倾向定位于一种底层的数据结构&#xff0c;记录的是一段连续的内存空间数据。但是在 Go 语言中平时直接用数组的时候不多&#xff0c;大多数场景下我…...

Ubuntu自启动设置

ubuntu中编写shell脚本开机自动启动(推荐)_Linux_脚本之家 1. vim test.sh 2. #!/bin/bash ### BEGIN INIT INFO # Provides: test # Required-Start: $remote_fs $syslog # Required-Stop: $remote_fs $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 …...

Qwen 通义千问 14B 模型,长文本问答效果测试

千问的config&#xff1a; seq_len2k max_position_embedding8k 注意&#xff0c;以下实验结果的字数是token数&#xff0c;不是中文字符数。 不使用动态ntk 12000字输入&#xff1a; 乱码5000字输入&#xff1a;乱码1500字输入&#xff1a;正常 不使用动态ntk&#xff0c…...

Prefix-Tuning源码解析

Prefix-Tuning源码解析 Prefix-Tuning在PEFT包中的源码实现 改写自Based on https://github.com/THUDM/P-tuning-v2/blob/main/model/prefix_encoder.py import torch from transformers import PretrainedConfigclass PrefixEncoder(torch.nn.Module):rThe torch.nn model t…...

Java EE-servlet API 三种主要的类

上述的代码如下&#xff1a; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.i…...

简单谈谈我参加数据分析省赛的感受与体会

数据分析省赛的感受与体会 概要考试前的感受与体会考试注意事项小结 概要 大数据分析省赛指的是在省级范围内举办的大数据分析竞赛活动。该竞赛旨在鼓励和推动大数据分析领域的技术创新和人才培养&#xff0c;促进大数据技术与应用的深度融合&#xff0c;切实解决实际问题。参…...

rust学习——泛型 (Generics)

文章目录 泛型 Generics泛型详解结构体中使用泛型枚举中使用泛型方法中使用泛型为具体的泛型类型实现方法 const 泛型&#xff08;Rust 1.51 版本引入的重要特性&#xff09;const 泛型表达式 泛型的性能 泛型 Generics Go 语言在 2022 年&#xff0c;就要正式引入泛型&#xf…...

【USRP】通信之有线通信

有线通信&#xff1a; 有线通信是指使用物理线路或媒体&#xff08;例如&#xff0c;铜线、同轴电缆、光纤&#xff09;进行数据、声音和视频传输的通信方式。由于它依赖于实体传输媒介&#xff0c;有线通信通常具有较高的稳定性和可靠性&#xff0c;并能支持长距离的高带宽通…...

【算法】BFS

BFS广度优先搜索 1. 概念理解 广度优先搜索(BFS)是指&#xff0c;以一个起点(原点、结点、根)为基本点&#xff0c;向其所要搜索的方向扩散&#xff0c;并最终到达目标点的搜索方法。 2. 应用方向 有迷宫问题、层序遍历等应用。 3. 迷宫问题 以迷宫问题为例。 当想要从左…...

ZYNQ7020开发(二):zynq linux系统编译

文章目录 一、编译前准备二、SDK编译三、编译步骤总结四、问题汇总 一、编译前准备 1.设置环境变量 source /opt/pkg/petalinux/2020.2/settings.sh/opt/pkg/petalinux/2020.2是上一节petalinux的安装目录 2.创建 petalinux 工程 进入petalinux安装目录(例如&#xff1a;/op…...

Kafka 自动配置部署信息的脚本记录

自动配置 Kafka 整理服务器内容时&#xff0c;发现一个测试 Kafka 的的一个脚本&#xff0c;它可以自动部署 Kafka &#xff0c;指定三个参数&#xff0c;完成 Kafka 的配置过程。 basePath$1 brokerId$2 zookeeperConnect$3 localIpifconfig |grep inet| awk {print $2}| he…...

数据分析入门

B站&#xff1a;01第一课 数据分析岗位职责和数据分析师_哔哩哔哩_bilibili 一、岗位&#xff1a;数据分析师 Q1 数据分析师在公司做什么工作&#xff1f; 数据来源于公司核心业务&#xff0c;通过监测业务健康度来确定业务的健康状况&#xff1b; 通过对用户精细化分析&am…...

车载网关通信能力解析——SV900-5G车载网关推荐

随着车联网的发展,各类车载设备对车载网关的需求日益增长。车载网关作为车与车、车与路、车与云之间连接的关键设备,其通信能力直接影响整个系统的性能。本文将详细解析车载网关的通信能力,并推荐性价比高的SV900-5G车载网关。 链接直达&#xff1a;https://www.key-iot.com/i…...

服务器中了mkp勒索病毒怎么处理,mkp勒索病毒解密,数据恢复

10月份以来&#xff0c;云天数据恢复中心陆续接到很多企业的求助&#xff0c;企业的服务器遭到了mkp勒索病毒攻击&#xff0c;导致企业的服务器数据库被加密&#xff0c;严重影响了企业工作&#xff0c;通过这一波mkp勒索病毒的攻击&#xff0c;云天数据恢复工程师为大家总结了…...

义乌再次位列第一档!2022年跨境电商综试区评估结果揭晓!

义乌跨境电商综试区捷报频传&#xff0c;在商务部公布的“2022年跨境电子商务综合试验区评估”结果中&#xff0c;中国&#xff08;义乌&#xff09;跨境电子商务综合试验区&#xff08;以下简称&#xff1a;“跨境综试区”&#xff09;评估结果为成效明显&#xff0c;综合排名…...

07、Python -- 序列相关函数与封包解包

目录 使用函数字符串也能比较大小序列封包序列解包多变量同时赋值 最大值、最小值、长度 序列解包与封包 使用函数 len()、max()、min() 函数可获取元组、列表的长度、最大值和最小值。 字符串也能比较大小 字符串比较大小时&#xff0c;将会依次按字符串中每个字符对应的编…...

# Spring 事务失效场景

Spring 事务失效场景 文章目录 Spring 事务失效场景前言事务不生效未开启事务事务方法未被Spring管理访问权限问题基于接口的代理源码解读 CGLIB代理 方法用final修饰同一类中的方法调用多线程调用不支持事务 事务不回滚设置错误的事务传播机制捕获了异常手动抛了别的异常自定义…...

华为OD 停车场车辆统计(100分)【java】A卷+B卷

华为OD统一考试A卷+B卷 新题库说明 你收到的链接上面会标注A卷还是B卷。目前大部分收到的都是B卷。 B卷对应20022部分考题以及新出的题目,A卷对应的是新出的题目。 我将持续更新最新题目 获取更多免费题目可前往夸克网盘下载,请点击以下链接进入: 我用夸克网盘分享了「华为O…...

出差学小白知识No6:LD_PRELOAD变量路径不对找不到库文件

交叉编译的时候出现以下问题&#xff0c;显示LD_PRELOAD变量找不到路劲 首先先查看一下LD_PRELOAD的路径&#xff1a;echo $LD_PRELOAD 如果输出一大串&#xff0c;那么先进行清空&#xff1a;unset LD_PRELOAD 重新给LD_PRELOAD进行赋值他的路径和库文件&#xff1a; expor…...

利用dns协议发起ddos反射攻击

利用DNS服务器发起反射型DDOS&#xff0c;攻击带宽 基本思路&#xff1a; 1、利用any类型的dns查询&#xff0c;可完成发送少量请求数据&#xff0c;获得大量返回数据。 2、将原请求地址改为受害者地址&#xff0c;则dns会向受害者返回大量数据&#xff0c;占用带宽 警告&…...

【力扣数据库知识手册笔记】索引

索引 索引的优缺点 优点1. 通过创建唯一性索引&#xff0c;可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度&#xff08;创建索引的主要原因&#xff09;。3. 可以加速表和表之间的连接&#xff0c;实现数据的参考完整性。4. 可以在查询过程中&#xff0c;…...

如何在看板中体现优先级变化

在看板中有效体现优先级变化的关键措施包括&#xff1a;采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中&#xff0c;设置任务排序规则尤其重要&#xff0c;因为它让看板视觉上直观地体…...

Docker 运行 Kafka 带 SASL 认证教程

Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明&#xff1a;server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...

【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密

在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...

电脑插入多块移动硬盘后经常出现卡顿和蓝屏

当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时&#xff0c;可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案&#xff1a; 1. 检查电源供电问题 问题原因&#xff1a;多块移动硬盘同时运行可能导致USB接口供电不足&#x…...

MVC 数据库

MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...

MODBUS TCP转CANopen 技术赋能高效协同作业

在现代工业自动化领域&#xff0c;MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步&#xff0c;这两种通讯协议也正在被逐步融合&#xff0c;形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...

Python爬虫(二):爬虫完整流程

爬虫完整流程详解&#xff08;7大核心步骤实战技巧&#xff09; 一、爬虫完整工作流程 以下是爬虫开发的完整流程&#xff0c;我将结合具体技术点和实战经验展开说明&#xff1a; 1. 目标分析与前期准备 网站技术分析&#xff1a; 使用浏览器开发者工具&#xff08;F12&…...

【HTML-16】深入理解HTML中的块元素与行内元素

HTML元素根据其显示特性可以分为两大类&#xff1a;块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...

自然语言处理——Transformer

自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效&#xff0c;它能挖掘数据中的时序信息以及语义信息&#xff0c;但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN&#xff0c;但是…...