go 切片slice学习总结

切片的结构

切片的底层结构:

type SliceHeader struct {Data uintptr  // 指向底层数组的指针 Len  int      //长度Cap  int      //空间容量
}

切片的初始化

1 通过数组或者已有的slice创建新的slice

1.1 使用数组创建切片

通过数组的一部分来初始化切片。

array := [10]int{0,1, 2, 3, 4, 5,6,7,8,9}  
slice := a[5:7] 

Slice将与原数组共用一部分内存。

1.2 通过slice创建新的切片对象

x := []int{2, 3, 5, 7, 11}  
y := x[1:3]

x:   长度len=5 cap=5 data指针指向长度为5的底层数组结构。

y  长度为2 cap为y底层原始数组结构第一个元素位置到的最后一个容量空间位置的长度即5-1=4

data为指针指向底层原始数组结构第1个元素的地址(索引从0开始)

图示如下:

2,使用make函数

make函数是Go中用于分配和初始化内置类型的内置函数,也可以用来创建切片。

slice := make([]int, 5, 10) // 创建一个长度为5、容量为10的int类型切片

长度为5,即可以使用下标slice[0] ~ slice[4]来操作里面的元素,capacity为10,表示后续向slice添加新的元素时可以不必重新分配内存,直接使用预留内存即可。

3,切片字面量

直接使用切片字面量创建切片。

s := []int{1, 2, 3} // 直接初始化一个切片


4,较特殊的切片:

nil 切片

var a []int 

未初始化的切片默认值为nil

空切片

var b = []int{} 

也叫零值切片。和 nil 不相等, 一般用来表示一个空的集合。len 和 cap 都为 0。空切片在内部拥有一个非nil的、零长度的底层数组


在判断一个切片是否为空时,一般通过 len 获取切片的长度来判断,一般很少将切片和 nil 值做直接的比较

数组与切片

数组截取元素生成切片,共用底层元素存储底层数组结构

func main() {data := [...]int{0, 1, 2, 3, 4, 5}s := data[2:4]s[0] += 100s[1] += 200fmt.Println(s)fmt.Println(data)
}

打印结果:

 [102 203]
 [0 1 102 203 4 5]

 切片赋值

	data := [...]int{0, 1, 2, 3, 4, 5}s2 := data[:]fmt.Println("s2是:", s2)s3 := s2fmt.Println("s3是:", s3)s3[0] = 1000 + s3[0]fmt.Println("s3是:", s3)fmt.Println("s2是:", s2)fmt.Println("data是:", data)

打印结果:

s3是: [1000 1 2 3 4 5]
s2是: [1000 1 2 3 4 5]
data是: [1000 1 2 3 4 5]

切片参数

函数参数是切片时,是引用传递,函数内对切片的改动影响源切片

    func main(){sl := []int{1, 2, 3, 4, 5, 6, 7}PrintElements(sl)fmt.Printf("slice a : %v\n", sl)}func PrintElements(sl []int) {fmt.Println("-------------")for i, v := range sl {fmt.Println(v)sl[i] = v + 1}
}

打印结果:

-------------

 1
2
3
4
5
6
7
slice a : [2 3 4 5 6 7 8]

切片容量

切片扩容

扩容实际上是重新分配一块更大的内存,将原Slice数据拷贝进新Slice,然后返回新Slice,扩容后再将数据追加进去。

例如,当向一个capacity为5,且length也为5的Slice再次追加1个元素时,就会发生扩容,如下图所示:

扩容操作只关心容量,会把原Slice数据拷贝到新Slice,追加数据由append在扩容结束后完成。上图可见,扩容后新的Slice长度仍然是5,但容量由5提升到了10,原Slice的数据也都拷贝到了新Slice指向的数组中。

扩容容量的选择遵循以下规则:

  • 如果原Slice容量小于1024,则新Slice容量将扩大为原来的2倍;
  • 如果原Slice容量大于等于1024,则新Slice容量将扩大为原来的1.25倍;

使用append()向Slice添加一个元素的实现步骤如下:

  • 假如Slice容量够用,则将新元素追加进去,Slice.len++,返回原Slice
  • 原Slice容量不够,则将Slice先扩容,扩容后得到新Slice
  • 将新元素追加进新Slice,Slice.len++,返回新的Slice。

参考:slice-地鼠文档 

切片字面量创建的切片

    slice := []int{1, 2, 3, 4, 5, 6, 7, 8}

当你使用字面量来创建切片时,切片的初始容量(capacity)通常等于其长度(length)。这意味着,例子slice := []int{1, 2, 3, 4, 5, 6, 7, 8},切片的长度和容量都是8。

	slice := []int{1, 2, 3, 4, 5, 6, 7, 8}fmt.Print("cap:", cap(slice))fmt.Print("len:", len(slice))

cap: 8
len: 8 

扩容:

	slice := []int{1, 2, 3, 4, 5, 6, 7, 8}fmt.Println("cap:", cap(slice))fmt.Println("len:", len(slice))fmt.Println("----------append后扩容----------")slice2 := append(slice, 0) // 切片扩展 1 个空间fmt.Println("cap:", cap(slice2))fmt.Println("len:", len(slice2))fmt.Println(&slice[0] == &slice2[0])

 打印结构

cap: 8
len: 8
----------append后扩容----------
cap: 16
len: 9
false

append元素后容量不够使用,扩容为原来的两倍

make创建的切片

make创建的切片,类型申明之后,第一个数值是长度,第二个数值是容量

slice2 := make([]int, 3, 7)fmt.Println("cap:", cap(slice2))fmt.Println("len:", len(slice2))

cap: 7
len: 3

append元素后,容量不够就会扩容为之前的两倍容量。随之是地址的改动。

	slice2 := make([]int, 3, 7)fmt.Println("cap:", cap(slice2))fmt.Println("len:", len(slice2))slice3 := append(slice2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) // 切片扩展 1 个空间fmt.Println("----------append后扩容----------")fmt.Println("cap:", cap(slice3))fmt.Println("len:", len(slice3))fmt.Println(&slice2[0] == &slice3[0])

cap: 7
len: 3
----------append后扩容----------
cap: 14
len: 13
false

切片操作

1,append操作

使用append函数,生成新的切片

append函数返回的slice跟原slice是独立的slice,互不影响

    var a = []int{1, 2, 3}fmt.Printf("slice a : %v\n", a)var b = []int{4, 5, 6}fmt.Printf("slice b : %v\n", b)c := append(a, b...)fmt.Printf("slice c  : %v\n", c)b = append(b, 7)fmt.Printf("slice b  after apend 7: %v\n", b)a = append(a, 7)fmt.Printf("slice a  after apend 7: %v\n", a)fmt.Printf("slice c : %v\n", c)c = append(c, 7)fmt.Printf("slice c  after apend 7: %v\n", c)

打印结果:

slice a : [1 2 3]
slice b : [4 5 6]
slice c  : [1 2 3 4 5 6]
slice b  after apend 7: [4 5 6 7]
slice a  after apend 7: [1 2 3 7]
slice c : [1 2 3 4 5 6]
slice c  after apend 7: [1 2 3 4 5 6 7]

其中b...是使用...展开运算符将slice展开作为单独元素传递给函数使用

以上apend操作是在末尾添加,还可以在头部添加

s := []int{2, 3, 4, 5, 6, 7}s = append([]int{1}, s...)fmt.Println("s是:", s)s = append([]int{-1, -2}, s...)fmt.Println("s是:", s)

s是: [1 2 3 4 5 6 7]
s是: [-1 -2 1 2 3 4 5 6 7] 

2,切片copy

copy(dst,src)将src内容复制给dst切片

src := []int{1, 2, 3}dst := make([]int, 3)copy(dst, src) // 现在dst是[1, 2, 3]的拷贝fmt.Println("dst是:", dst)

copy复制后两个切片是独立的

src := []int{1, 2, 3}dst := make([]int, 3)copy(dst, src) // 现在dst是[1, 2, 3]的拷贝fmt.Println("dst是:", dst)dst = append(dst, 4)fmt.Println("dst是:", dst)fmt.Println("src是:", src)

 dst是: [1 2 3]
dst是: [1 2 3 4]
src是: [1 2 3]

copy与append实现高效 添加元素

a = append(a, 0)     // 切片扩展 1 个空间copy(a[i+1:], a[i:]) // a[i:] 向后移动 1 个位置a[i] = x             // 设置新添加的元素

具体实现:

a := []int{1, 2, 3, 4, 5, 6, 7, 8}a = append(a, 0)     // 切片扩展 1 个空间copy(a[4+1:], a[4:]) // a[i:] 向后移动 1 个位置a[4] = 88            // 设置新添加的元素fmt.Println("a是:", a)

打印:

a是: [1 2 3 4 88 5 6 7 8] 

3,切片删除 

从末尾删除

slice5 := []int{1, 2, 3, 4, 5, 6, 7, 8}new_slice5 := slice5[:len(slice5)-1]fmt.Println("new_slice5:", new_slice5)new_slice5 = slice5[:len(slice5)-5]fmt.Println("new_slice5:", new_slice5)

new_slice5: [1 2 3 4 5 6 7]
new_slice5: [1 2 3]

从头部删除 

    slice5 = slice5[1:]  //删除头部第1个元素

    slice5 = slice[5:]   //删除头部第N个元素

使用append删除

append删除元素非常灵活,可以删除头部,中间,尾部元素。注意若是希望地址不变,需要将append的结果赋值回去

 删除头部元素

slice5 = append(slice5[:0], slice5[3:]...) //删除头部元素fmt.Println("slice5:", slice5)

slice5: [4 5 6 7 8]

删除中间元素

	slice5 = append(slice5[:3], slice5[5:]...) //删除中间两个元素fmt.Println("slice5:", slice5)

slice5: [1 2 3 6 7 8]

删除尾部元素

slice5 = append(slice5[:0], slice5[0:len(slice5)-3]...) //删除尾部元素fmt.Println("slice5:", slice5)

slice5: [1 2 3 4 5] 

以上便是对slice的学习总结,若是不当之处,烦请指出。后面有新的学习领悟,会继续添加。 

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

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

相关文章

数据结构-c/c++实现栈(详解,栈容量可以动态增长)

一.栈的基本介绍 栈是一种只能够在一端进行插入和删除的顺序表。如下图 空栈:表示不含任何元素的栈 栈顶:表示允许进行插入和删除元素的一端 栈底:表示不允许进行插入和删除元素的一端 即栈是一种后进先出的线性表数据结构 二.栈的常见操…

为什么我的手机卡需要快递员给激活?这到底安全吗?

网友咨询:网上申请了一张新卡,本来想着自己激活,没想到快递员先打电话过来说,要身份证给帮助激活,所以我想问一下,网上申请的卡是不是都是快递给激活呢?安不安全呢? 首先要说一下&a…

第4章-08-用Python Requests库模拟浏览器访问接口

🏆作者简介,黑夜开发者,CSDN领军人物,全栈领域优质创作者✌,CSDN博客专家,阿里云社区专家博主,2023年CSDN全站百大博主。 🏆数年电商行业从业经验,历任核心研发工程师,项目技术负责人。 🏆本文已收录于专栏:Web爬虫入门与实战精讲,后续完整更新内容如下。 文章…

CSRF漏洞的预防

目录 CSRF漏洞预防措施 深入研究 CSRF Token的工作原理是什么? 为什么仅依靠Referer头字段来防范CSRF攻击不是完全可靠? SameSite cookie属性如何防止CSRF攻击? SameSite Cookie属性的作用 如何通过SameSite属性防止CSRF攻击 导图 CS…

Eclipse 自定义字体大小

常用编程软件自定义字体大全首页 文章目录 前言具体操作1. 打开设置对话框2. 打开字体设置页面3. 找到Text Font,点击修改4. 修改字体 前言 Eclipse 自定义字体大小,统一设置为 Courier New ,大小为 三号 具体操作 【Windows】>【Perfer…

Qt第二课----信号和槽

作者前言 🎂 ✨✨✨✨✨✨🍧🍧🍧🍧🍧🍧🍧🎂 ​🎂 作者介绍: 🎂🎂 🎂 🎉🎉&#x1f389…

C#笔记4 详细解释事件及其原型、匿名方法和委托的关系

匿名方法 定义 匿名方法允许一个与委托关联的代码被内联的写入使用委托的位置。 语法形式 delegate(参数列表) {代码块 } 前文说过,委托是定义了一个公司,公司专门承接某一类型的任务。 委托的实例化就是公司把任务交给了具体的职员(方…

掌握测试的艺术:深入探索Python的pytest库

文章目录 **掌握测试的艺术:深入探索Python的pytest库**背景:为什么选择pytest?pytest是什么?如何安装pytest?5个简单的库函数使用方法1. pytest.main()2. pytest.skip()3. pytest.mark.parametrize()4. pytest.raises…

基于物联网的低成本便携式传感器节点用于火灾和空气污染的检测与报警

目录 摘要 引言 材料和方法 传感器节点 IoT 微控制器 颗粒物传感器 环境和气体传感器 MQTT代理 Node-Red监控平台 系统结构 数据存储 工作描述 实验结果 讨论 结论 致谢 参考文献 这篇论文的标题是《Low-cost IoT-based Portable Sensor Node for Fire and Air…

STM32G474之TIM1捕获1模式

STM32G474采用TIM8产生方波信号,使用TIM1工作于捕获1模式,并计算方波频率。捕获方波周期,在有些开发中,还是能用到。建议开发时使用HAL库自带的库函数。使用寄存器方法也可以实现,但是后期修改不太方便。 测试时&…

利用 Web 浏览器构建 Java Media Player

如果您需要在 Java 桌面应用程序中嵌入媒体播放器,有几种方法可供选择: 您可以使用 JavaFX Media API 来实现所有必需的媒体播放器功能。虽然稍显过时但仍然可用的 Java Media Framework 也可以作为一种解决方案。您可以集成像 VLCJ 这样的第三方 Java …

如何选择适合企业的财税自动化解决方案

财税自动化解决方案是现代企业提升财务管理效率、降低运营成本的关键工具。然而,市场上的财税自动化产品琳琅满目,功能各异,企业在选择时常常感到困惑。本文金智维将从中小型的需求出发,帮助企业了解如何选择适合自身的财税自动化…

QT实战项目之音乐播放器

项目效果演示 myMusicShow 项目概述 在本QT音乐播放器实战项目中,开发环境使用的是QT Creator5.14版本。该项目实现了音乐播放器的基本功能,例如开始播放、停止播放、下一首播放、上一首播放、调节音量、调节倍速、设置音乐播放模式等。同时还具备搜索功能,通过搜索歌曲名字…

另一种关于类的小例

前言 我们还是以一段关于构造函数的代码作为开端,我们以之前银行家的小项目为背景 class Account {constructor(owner, currency, pin) {this.owner owner;this.currency currency;this.pin pin;} }const ITshare new Account(ITshare, EUR, 21211); console.…

零基础入门天池镜像提交--windows场景VirtualBox虚拟机安装linux系统并ssh远程登录,直至镜像的制作及提交

背景:由于本人只有一台windows,天池上的比赛需要提交镜像,自己试了好多方法给windows安装linux,但是始终没安装成功。最终采用在利用VirtualBox安装linux虚拟机,使用MobaXterm进行ssh登陆linux,镜像的制作、push、提交…

Java+Swing实现学生选课管理系统

JavaSwing实现学生选课管理系统 一、系统介绍二、系统展示1.课程查询2.课程添加3.退课 三、系统实现四、其他1.其它系统2.获取源码 一、系统介绍 本系统实现了学生登录和管理员登录,学生实现选课,查看已选课程,修改密码,查看学生…

Java | Leetcode Java题解之第386题字典序排数

题目&#xff1a; 题解&#xff1a; class Solution {public List<Integer> lexicalOrder(int n) {List<Integer> ret new ArrayList<Integer>();int number 1;for (int i 0; i < n; i) {ret.add(number);if (number * 10 < n) {number * 10;} els…

顺序表

目录 1. 数据结构 2. 顺序表 1&#xff09;线性表 2&#xff09;顺序表分类 3、动态顺序表的实现 1. 数据结构 数据&#xff1a;常见的数值1、2、3、4.....、教务系统里保存的用户信息&#xff08;姓名、性别、年龄、学历等 等&#xff09;、网页里肉眼可以看到的信息&…

vTESTstudio系列12--vTESTstudio中的动态函数库介绍2

在上期的文章&#xff08;vTESTstudio系列11--vTESTstudio中的动态函数库介绍1&#xff09;中&#xff0c;我们详细介绍了osek_tp.dll的接口&#xff0c;本章开始给大家介绍如何通过osek_tp.dll的接口去发送诊断指令&#xff0c;Let‘s Go!!! 目录 1. CanTp发送数据的函数&am…

图表操作——图表保存为图片+多个图表批量保存为压缩包——js技能提升

使用场景&#xff1a; echarts图表&#xff1a;生成的柱状图/折线图/饼图等可以实现图表的导出&#xff0c;导出格式为一个图片。也可以支持多个图表同时导出为图片&#xff0c;以压缩包的形式下载下来。 下面介绍单个导出批量导出的具体用法&#xff1a; 1.单个导出功能——…