go slice切片的详细知识(包含底层扩容)——2

目录

例子

例3:使用append逐个添加元素和一次性添加多个元素的区别

例4:order[low:high:max]

例5:当容量大于1024的时候,每次扩容真的是1.25倍吗?


本文是对上一篇文章的补充:

go slice切片的详细知识(包含底层扩容)-CSDN博客


例子

例3:使用append逐个添加元素和一次性添加多个元素的区别

一次性添加多个元素:slice = append(slice, 1, 2, 3)

  • 性能:可能更高效,因为只进行了一次内存分配和复制操作。
  • 代码简洁:代码更简洁明了。

逐个添加元素:slice = append(slice, 1)、slice = append(slice, 2)、slice = append(slice, 3) 

  • 性能:可能较低,因为每次添加元素时,切片可能需要多次进行内存分配和复制操作(取决于底层数组的容量)。
  • 内存分配:可能会多次触发内存分配操作。
  • 代码冗长:代码较为冗长,不如一次性添加多个元素的方式简洁。
// 示例 1: 一次性添加多个元素
var s1 []int
s1 = append(s1, 1, 2, 3)
fmt.Printf("%p %v %d %d\n", s1, s1, len(s1), cap(s1)) // 0xc000020090 [1 2 3] 3 3var slice1 []int = []int{1, 2}
fmt.Printf("%p %v %d %d\n", slice1, slice1, len(slice1), cap(slice1)) // 0xc000128010 [1 2] 2 2
slice1 = append(slice1, 3, 4, 5)
fmt.Printf("%p %v %d %d\n", slice1, slice1, len(slice1), cap(slice1)) // 0xc000120060 [1 2 3 4 5] 5 6// 示例 2: 逐个添加元素
var slice2 []int = []int{1, 2}
fmt.Printf("%p %v %d %d\n", slice2, slice2, len(slice2), cap(slice2)) // 0xc000128060 [1 2] 2 2
slice2 = append(slice2, 3)
fmt.Printf("%p %v %d %d\n", slice2, slice2, len(slice2), cap(slice2)) // 0xc00012c020 [1 2 3] 3 4
slice2 = append(slice2, 4)
fmt.Printf("%p %v %d %d\n", slice2, slice2, len(slice2), cap(slice2)) // 0xc00012c020 [1 2 3 4] 4 4
slice2 = append(slice2, 5)
fmt.Printf("%p %v %d %d\n", slice2, slice2, len(slice2), cap(slice2)) // 0xc00012e000 [1 2 3 4 5] 5 8

内容最终相同,但它们的内存分配情况可能不同。多次调用 append 可能会导致多次内存分配,而一次性添加多个元素则可能只需要进行一次内存分配和复制。

例4:order[low:high:max]

切片在被截取时的另一个特点是,被截取后的数组仍然指向原始切片的底层数据。

如:bar 执行了 append 函数之后,最终也修改了 foo 的最后一个元素,这是一个在实践中非常常见的陷阱。

foo := []int{0, 0, 0, 42, 100}
bar := foo[1:4]fmt.Println(len(foo), cap(foo), foo) // 5 5 [0 0 0 42 100]
fmt.Println(len(bar), cap(bar), bar) // 3 4 [0 0 42]fmt.Printf("%p %p\n", foo, bar) // 0xc00001c1b0 0xc00001c1b8  虽然地址不同(切片结构体还有长度和容量属性,所以切片结构体地址不同),但是指向的底层数组是同一个,因为没有扩容bar = append(bar, 99)
fmt.Println(len(foo), cap(foo), foo) // 5 5 [0 0 0 42 99]
fmt.Println(len(bar), cap(bar), bar) // 4 4 [0 0 42 99]

如果要解决这样的问题,其实可以在截取时指定容量:order[low:high:max]

foo := []int{0, 0, 0, 42, 100}
bar := foo[1:4:4]
// bar := foo[1:4:3] // 报错:Invalid index values, must be low <= high <= maxfmt.Println(len(foo), cap(foo), foo) // 5 5 [0 0 0 42 100]
fmt.Println(len(bar), cap(bar), bar) // 3 3 [0 0 42]fmt.Printf("%p %p\n", foo, bar) // 0xc00001c1b0 0xc00001c1b8bar = append(bar, 99)
fmt.Println(len(foo), cap(foo), foo) // 5 5 [0 0 0 42 100]
fmt.Println(len(bar), cap(bar), bar) // 4 6 [0 0 42 99]

解释foo[1:4:4]:

  • low 是 1,表示新切片从 foo 的索引 1 开始(包含这个元素)。
  • high 是 4,表示新切片到 foo 的索引 4 结束(不包含这个元素)。
  • max 是 4,表示新切片的容量是从 low 开始到 max 结束的长度。

练习:

sliceA := make([]int, 5, 10)
sliceB := sliceA[0:5]
sliceC := sliceA[0:5:5]
fmt.Println(len(sliceA), cap(sliceA)) // 5 10
fmt.Println(len(sliceB), cap(sliceB)) // 5 10
fmt.Println(len(sliceC), cap(sliceC)) // 5 5
orderLen := 5
order := make([]uint16, 2*orderLen)pollorder := order[:orderLen:orderLen]
lockorder := order[orderLen:][:orderLen:orderLen]
// pollorder切片指的是order的前半部分切片,lockorder指的是order的后半部分切片,即原order分成了两段。所以,pollorder和lockerorder的长度和容量都是5。
fmt.Println(len(pollorder), cap(pollorder)) // 5 5
fmt.Println(len(lockorder), cap(lockorder)) // 5 5
sli := make([]int, 0)
sli = append(sli, []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}...)
s := sli[5:][:5] // sli 和 s 共享同一个底层数组
// sli[5:]:[6, 7, 8, 9, 10]
// [:5]:从上述新切片中再取前5个元素,结果是[6, 7, 8, 9, 10]fmt.Println(s) // [6 7 8 9 10]s[0] = 111
fmt.Println(s, sli) // [111 7 8 9 10] [1 2 3 4 5 111 7 8 9 10]

例5:当容量大于1024的时候,每次扩容真的是1.25倍吗?

1024 直接扩容到 1536,不是1.25倍,而是1.5倍,这是为什么?(对于容量大于等于 1024 的切片,在扩容时 Go 并不是简单地按照1.25倍扩容,而是使用了一种更复杂的策略。)

s2 := make([]int, 1024)
fmt.Printf("s2: len: %d, cap: %d\n", len(s2), cap(s2)) // s2: len: 1024, cap: 1024
s2 = append(s2, 1)
fmt.Printf("s2: len: %d, cap: %d\n", len(s2), cap(s2)) // s2: len: 1025, cap: 1536

向 slice 追加元素的时候,若容量不够,会调用 growslice 函数:

func growslice(et *_type, old slice, cap int) slice {// ……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}// ……capmem = roundupsize(uintptr(newcap) * ptrSize)newcap = int(capmem / ptrSize)
}

for循环:会不断循环直到 newcap 超过所需的容量。最终的结果可能会比严格的1.25倍略大一些,具体取决于当前的容量和内存分配的优化策略。

最后两行代码:对 newcap 作了一个内存对齐,这个和内存分配策略相关,所以最终结果不一定是 1.25的整数倍(有时候扩容和元素类型的字节数有关系)。

Go 语言的切片扩容机制是相当复杂的,它考虑了多种因素来确定新的容量,以便在性能和内存使用之间找到平衡。Go 语言的 runtime 库在执行切片扩容时,有时会为了减少频繁的内存分配而使用稍大的倍数。这种优化主要是为了减少内存分配次数,提高性能。

Go 语言中切片扩容的策略为:

  • 如果新申请容量(cap)大于旧容量(old.cap)的两倍,则最终容量(newcap)是新申请的容量(cap);
  • 如果旧切片的长度小于 1024,则最终容量是旧容量的 2 倍,即“newcap=doublecap”;
  • 如果旧切片的长度大于或等于 1024,则最终容量从旧容量开始循环增加原来的 1/4,直到最终容量大于或等于新申请的容量为止;
  • 如果最终容量计算值溢出,即超过了 int 的最大范围,则最终容量就是新申请容量。

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

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

相关文章

离轴磁编案例分享 - 机器人关机模组

客户产品 六轴协作机器人产品 关机模组 关机模组内部结构 项目介绍 客户需求: 需要离轴&#xff0c;优点&#xff1a;可以中空走线&#xff0c;方便线缆从机器人中间穿过去&#xff0c;可以更好得保护好线缆&#xff0c;不需要把线漏在外面&#xff0c;影响使用和产品寿命。目…

最适合上班族和宝妈的兼职副业,一天500多,小众副业项目

近年来&#xff0c;地方特色小吃逐渐受到人们的热烈追捧&#xff0c;尤其是在直播的助力下&#xff0c;许多地方的特色小吃得以走进大众视野&#xff0c;吸引了大量流量和人气。因此&#xff0c;有很大一部分商家和创业者看准了这一商机&#xff0c;纷纷投身于地方特色小吃的制…

怎么把多种内容做成二维码?扫码展现多种内容的制作方法

现在很多的场景下都有不同类型的二维码&#xff0c;用来承载内容为用户提供内容展示&#xff0c;比如图片、视频、文字、文件、地图等等内容&#xff0c;都可以组合起来通过扫码的方式在手机上展示。那么如何制作组合内容的二维码相信有很多的小伙伴都非常的感兴趣。 其实二维…

众汇:外汇狙击指标如何使用?

对于投资者来说&#xff0c;我们各位交易的目的是什么?WeTrade众汇认为那就是盈利。所以来说有一个指标对各位投资者来说那是相当有帮助的。这是因为对于交易者而言&#xff0c;利用这些指标可以快速识别盈利的买卖时机。当我们选择一个指标之后&#xff0c;深入了解其适用范围…

【SpringBoot】打包成Docker镜像后日志输出中文乱码

解决方法 配置文件中对日志的配置添加如下选项 logging:charset:file: UTF-8console: UTF-8注:如果只需要解决控制台乱码,则不需要file这一项

「布道师系列文章」众安保险王凯解析 Kafka 网络通信

作者&#xff5c;众安保险基础平台 Java 开发专家王凯 引言 今天给大家带来的是 Kafka 网路通信主要流程的解析&#xff08;基于 Apache Kafka 3.7[2]&#xff09;。同时引申分析了业界当前较火的AutoMQ基于Kafka在网络通信层面的优化和提升。 01 如何构建一个基本的请求…

学习笔记(一)——Langchain基本操作与函数

学习笔记(一)——Langchain基本操作与函数 目录 学习笔记(一)——Langchain基本操作与函数基本初始化配置LangsmithLanguage Models 基础指令传递信息OutputParsers 输出解析器chain 链Prompt Templates 提示模板Message History 消息历史记录Managing Conversation History 管…

【机器学习】之 kmean算法原理及实现

基本概念 K-Means 聚类算法的目标是将数据集分成 ( K ) 个簇&#xff0c;使得每个簇内的数据点尽可能相似&#xff0c;而簇与簇之间尽可能不同。这种相似度是通过计算数据点与簇中心的距离来衡量的。 算法步骤 选择簇的数量 ( K )&#xff1a;随机选择 ( K ) 个数据点作为初…

XL7005A SOP-8 0.4A1.25-20V 150KHz降压直流转换器芯片

XL7005A作为一款高性能的降压型电源管理芯片&#xff0c;在智能家居中有着广泛的应用。以下是一些具体的案例&#xff1a; 1. 智能灯具&#xff1a;XL7005A可用于控制LED灯的电源&#xff0c;提供稳定高效的电源支持&#xff0c;确保灯具亮度稳定且无频闪&#xff0c;提高用户体…

springboot从2.7.2 升级到 3.3.0

文章目录 概要准备报错调整小结后记 概要 时代在进步&#xff0c;springboot已经来到了3.3.0 , 于是我们也打算升级下sbvadmin到3.3&#xff0c; jdk使用21的版本&#xff0c;下面是升级过程中碰到的一些问题&#xff0c;问题不大。 2.7.2 -> 3.3.0 准备 下载jdk21&#…

接口框架项目实战-pytest(六)csv数据驱动

csv 数据驱动 为了解决数据量大 导致yaml文件重复太多 yaml_util.py import osimport jsonpath import yamlfrom pytestdemo.common.base_util import get_path from pytestdemo.common.csv_util import analysis_parametersdef read_config_file(one_node,two_node):with ope…

windows的软件修改图标

要修改一个可执行文件&#xff08;.exe&#xff09;的图标&#xff0c;你可以使用 Resource Hacker 这样的工具。Resource Hacker 是一个免费的资源编辑器&#xff0c;可以用于修改和编辑 Windows 可执行文件中的资源。 以下是一个简单的步骤来修改一个 .exe 文件的图标&#x…

shell脚本 字符串拼接变量赋值失效

问题现象&#xff1a; 代码如下&#xff1a; 执行结果&#xff1a; 可以看到data_dir属性是有值的&#xff0c;但是做字符串拼接变量赋值失效了很奇怪 怀疑赋值哪里写错了 问题分析&#xff1a; 1. 还是觉得赋值没有问题&#xff0c;手动显式赋值再执行下 执行结果&#…

职场如同“染缸”,老板只是给你个平台,染的好坏,全凭运气!

无论哪个单位&#xff0c;在职场大染缸里总有那么一拨同事是你喜欢的&#xff0c;也有那么一拨同事是不痛不痒的&#xff0c;还有那么一拨同事却是你怎么看都觉得不顺眼的。“不顺眼”的定义很宽泛&#xff0c;可能是他曾经的一些言论触及了你的道德底线&#xff0c;可能是他的…

100vh问题及解决方案

100vh 问题通常出现在移动端浏览器中&#xff0c;尤其是当你使用 100vh 设定元素高度时。这是因为移动端浏览器在显示视口高度时会包括地址栏和工具栏&#xff0c;这些栏在滚动时会隐藏&#xff0c;从而导致视口高度发生变化&#xff0c;影响布局。 具体问题 高度不稳定&…

C++数据结构之:堆Heap

摘要&#xff1a; it人员无论是使用哪种高级语言开发东东&#xff0c;想要更高效有层次的开发程序的话都躲不开三件套&#xff1a;数据结构&#xff0c;算法和设计模式。数据结构是相互之间存在一种或多种特定关系的数据元素的集合&#xff0c;即带“结构”的数据元素的集合&am…

【Qt】win10,QTableWidget表头下无分隔线的问题

1. 现象 2. 原因 win10系统的UI样式默认是这样的。 3. 解决 - 方法1 //横向表头ui->table->horizontalHeader()->setStyleSheet("QHeaderView::section{""border-top:0px solid #E5E5E5;""border-left:0px solid #E5E5E5;""bord…

Python与Excel的完美结合:操作技巧与自动化应用

本文全面探讨了使用Python进行Excel操作的各种技巧和自动化应用&#xff0c;包括基础操作、高级数据处理、图表创建、自动化任务等。通过详细介绍Python在Excel中的应用&#xff0c;结合实际案例&#xff0c;帮助读者深入理解并掌握Python处理Excel文件的强大功能。 文章目录 P…

Matlab|【重磅】配电网故障重构/孤岛划分

目录 1 主要内容 1.1 背景 1.2 流程图 2 部分代码 3 程序结果 4 下载链接 1 主要内容 程序主要复现《基于GA_BFGS算法的配电网故障恢复性重构研究_郑海广》&#xff0c;采用matlab编程软件实现&#xff0c;依据网络结构和DG供电方式对配电网进行孤岛划分&#xff0c;将含…

政府窗口服务第三方评估报告如何写

撰写政府窗口服务第三方评估报告需要结构清晰、内容详实&#xff0c;并包含对评估过程和结果的详细描述以及改进建议。以下是第三方评估机构民安智库&#xff08;第三方社会评估调研公司&#xff09;给出的一个政府窗口服务第三方评估报告简单的示例&#xff1a; 一、封面 报…