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
有的语言会把数组用作常用的基本的数据结构,比如 JavaScript,而 Golang 中的数组(Array),更倾向定位于一种底层的数据结构,记录的是一段连续的内存空间数据。但是在 Go 语言中平时直接用数组的时候不多,大多数场景下我…...
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: seq_len2k max_position_embedding8k 注意,以下实验结果的字数是token数,不是中文字符数。 不使用动态ntk 12000字输入: 乱码5000字输入:乱码1500字输入:正常 不使用动态ntk,…...
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 三种主要的类
上述的代码如下: 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…...

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

rust学习——泛型 (Generics)
文章目录 泛型 Generics泛型详解结构体中使用泛型枚举中使用泛型方法中使用泛型为具体的泛型类型实现方法 const 泛型(Rust 1.51 版本引入的重要特性)const 泛型表达式 泛型的性能 泛型 Generics Go 语言在 2022 年,就要正式引入泛型…...
【USRP】通信之有线通信
有线通信: 有线通信是指使用物理线路或媒体(例如,铜线、同轴电缆、光纤)进行数据、声音和视频传输的通信方式。由于它依赖于实体传输媒介,有线通信通常具有较高的稳定性和可靠性,并能支持长距离的高带宽通…...
【算法】BFS
BFS广度优先搜索 1. 概念理解 广度优先搜索(BFS)是指,以一个起点(原点、结点、根)为基本点,向其所要搜索的方向扩散,并最终到达目标点的搜索方法。 2. 应用方向 有迷宫问题、层序遍历等应用。 3. 迷宫问题 以迷宫问题为例。 当想要从左…...

ZYNQ7020开发(二):zynq linux系统编译
文章目录 一、编译前准备二、SDK编译三、编译步骤总结四、问题汇总 一、编译前准备 1.设置环境变量 source /opt/pkg/petalinux/2020.2/settings.sh/opt/pkg/petalinux/2020.2是上一节petalinux的安装目录 2.创建 petalinux 工程 进入petalinux安装目录(例如:/op…...
Kafka 自动配置部署信息的脚本记录
自动配置 Kafka 整理服务器内容时,发现一个测试 Kafka 的的一个脚本,它可以自动部署 Kafka ,指定三个参数,完成 Kafka 的配置过程。 basePath$1 brokerId$2 zookeeperConnect$3 localIpifconfig |grep inet| awk {print $2}| he…...

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

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

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

义乌再次位列第一档!2022年跨境电商综试区评估结果揭晓!
义乌跨境电商综试区捷报频传,在商务部公布的“2022年跨境电子商务综合试验区评估”结果中,中国(义乌)跨境电子商务综合试验区(以下简称:“跨境综试区”)评估结果为成效明显,综合排名…...

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

# Spring 事务失效场景
Spring 事务失效场景 文章目录 Spring 事务失效场景前言事务不生效未开启事务事务方法未被Spring管理访问权限问题基于接口的代理源码解读 CGLIB代理 方法用final修饰同一类中的方法调用多线程调用不支持事务 事务不回滚设置错误的事务传播机制捕获了异常手动抛了别的异常自定义…...
华为OD 停车场车辆统计(100分)【java】A卷+B卷
华为OD统一考试A卷+B卷 新题库说明 你收到的链接上面会标注A卷还是B卷。目前大部分收到的都是B卷。 B卷对应20022部分考题以及新出的题目,A卷对应的是新出的题目。 我将持续更新最新题目 获取更多免费题目可前往夸克网盘下载,请点击以下链接进入: 我用夸克网盘分享了「华为O…...

出差学小白知识No6:LD_PRELOAD变量路径不对找不到库文件
交叉编译的时候出现以下问题,显示LD_PRELOAD变量找不到路劲 首先先查看一下LD_PRELOAD的路径:echo $LD_PRELOAD 如果输出一大串,那么先进行清空:unset LD_PRELOAD 重新给LD_PRELOAD进行赋值他的路径和库文件: expor…...

利用dns协议发起ddos反射攻击
利用DNS服务器发起反射型DDOS,攻击带宽 基本思路: 1、利用any类型的dns查询,可完成发送少量请求数据,获得大量返回数据。 2、将原请求地址改为受害者地址,则dns会向受害者返回大量数据,占用带宽 警告&…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...

docker详细操作--未完待续
docker介绍 docker官网: Docker:加速容器应用程序开发 harbor官网:Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台,用于将应用程序及其依赖项(如库、运行时环…...
渲染学进阶内容——模型
最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...
【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验
系列回顾: 在上一篇中,我们成功地为应用集成了数据库,并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了!但是,如果你仔细审视那些 API,会发现它们还很“粗糙”:有…...
OpenLayers 分屏对比(地图联动)
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...

ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...
C++八股 —— 单例模式
文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全(Thread Safety) 线程安全是指在多线程环境下,某个函数、类或代码片段能够被多个线程同时调用时,仍能保证数据的一致性和逻辑的正确性…...

Linux --进程控制
本文从以下五个方面来初步认识进程控制: 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程,创建出来的进程就是子进程,原来的进程为父进程。…...
rnn判断string中第一次出现a的下标
# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...

10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...