【golang学习之旅】复杂数据类型——切片(slice)

2024-08-25 23:04

本文主要是介绍【golang学习之旅】复杂数据类型——切片(slice),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

系列文章

【golang学习之旅】使用VScode安装配置Go开发环境
【golang学习之旅】报错:a declared but not used
【golang学习之旅】Go 的基本数据类型
【golang学习之旅】深入理解字符串string数据类型
【golang学习之旅】go mod tidy
【golang学习之旅】记录一次 panic case : reflect: reflect.Value.SetInt using unaddressable value
【golang学习之旅】记录一次 error case : full error output: cc1: error: unrecognized command line option
【golang学习之旅】Go程序快速开始 & Go程序开发的基本注意事项
【golang学习之旅】Go语言常用转义字符
【golang学习之旅】Go中的变量(1)
【golang学习之旅】Go中的变量——基本数据类型(1)
【golang学习之旅】Go中的变量——基本数据类型(2)
【golang学习之旅】复杂数据类型——指针 & 函数
【golang学习之旅】延迟调用——defer
【golang学习之旅】复杂数据类型——数组


  • 系列文章
  • 1. 理解切片(slice)
  • 2. 切片的定义
    • 2.1 切片的内存结构
    • 2.2 创建切片
  • 3. 操作切片
    • 3.1 添加切片元素
      • 3.1.1 append
      • 3.1.2 copy
    • 3.2 删除切片元素


1. 理解切片(slice)

切片(slice)是对数组一个连续片段的引用(该数组我们称之为相关数组,通常是匿名的),所以切片是一个引用类型(因此更类似于 C/C++ 中的数组类型,或者 Python 中的 list 类型)。这个片段可以是整个数组,或者是由起始和终止索引标识的一些项的子集

简单地说,切片就是一种简化版的动态数组

因为动态数组的长度是不固定,切片的长度自然也就不能是类型的组成部分了。数组虽然有适用它们的地方,但是数组的类型和操作都不够灵活,因此在 Go 代码中数组使用的并不多。而切片则使用得相当广泛,理解切片的原理和用法是一个 Go 程序员的必备技能。


2. 切片的定义

2.1 切片的内存结构

我们先看看切片的结构定义,reflect.SliceHeader

切片在内存中的组织方式实际上是一个有 3 个域的结构体:指向相关数组的指针,切片长度以及切片容量。

type SliceHeader struct {  Data uintptr // 指向底层数组的指针  Len  int     // 切片的长度(即元素的数量)  Cap  int     // 切片的容量(即底层数组可以容纳的元素数量)  
}

现在,考虑以下两个切片的声明和初始化:

x := []int{2, 3, 5, 7, 11}  
y := x[1:3]
  • x切片的内存结构
    • Data:指向一个包含至少5个int类型元素的数组的指针。具体地,这个数组包含元素[2, 3, 5, 7, 11]。
    • Len:5,因为x包含5个元素。
    • Cap:5,因为底层数组也恰好有5个元素的空间,所以切片x的容量等于其长度。
  • y切片的内存结构
    • Data:同样指向那个包含[2, 3, 5, 7, 11]的数组,但切片y的视角是从第二个元素开始的,即3(索引为1,因为索引从0开始)。
    • Len:2,因为y是通过x[1:3]创建的,所以它包含x中从索引1到索引2(不包含3)的元素,即[3, 5]。
      Cap:4,尽管y只包含了2个元素,但它的容量是从y的起始元素到原数组x末尾的元素数量,即[3, 5, 7, 11]这四个元素的空间。这是因为y和x共享同一个底层数组,所以y的容量受到这个共享数组的限制。
  • 示意图
    下面是一个简化的示意图,用于说明这两个切片的内存结构:
    在这里插入图片描述

2.2 创建切片

在Go中,我们可以通过多种方式创建切片:

  1. 直接使用数组创建切片:通过数组的一部分来初始化切片。
a := [5]int{1, 2, 3, 4, 5}  
s := a[1:4] // s是一个切片,包含a中索引为1到3的元素(即2, 3, 4)
  1. 使用make函数:make函数是Go中用于分配和初始化内置类型的内置函数,也可以用来创建切片。
s := make([]int, 0, 5) // 创建一个长度为0、容量为5的int类型切片
  1. 切片字面量:直接使用切片字面量创建切片。
s := []int{1, 2, 3} // 直接初始化一个切片

还有一些比较特殊的切片:

  1. nil 切片。未初始化的切片默认值为nil
var a []int 
  1. 空切片,也叫零值切片。和 nil 不相等, 一般用来表示一个空的集合。len 和 cap 都为 0。空切片在内部拥有一个非nil的、零长度的底层数组
var b = []int{} 
  • 在判断一个切片是否为空时,一般通过 len 获取切片的长度来判断,一般很少将切片和 nil 值做直接的比较

3. 操作切片

3.1 添加切片元素

3.1.1 append

使用append函数可以向切片末尾追加一个或多个元素。如果追加的元素超出了当前切片的容量,append会分配一个新的底层数组,并将原切片的内容以及新元素复制到新数组中。

var a []int
a = append(a, 1)               // 追加 1 个元素
a = append(a, 1, 2, 3)         // 追加多个元素, 手写解包方式
a = append(a, []int{1,2,3}...) // 追加 1 个切片, 切片需要解包

这里需要注意...(三个点,称为“变参”或“展开运算符”)。在Go中,当你将切片或数组作为函数参数,并且希望将它的所有元素作为独立的参数传递给函数时,可以使用这个操作符。在这个上下文中,...[]int{1,2,3} 切片中的所有元素“展开”成独立的参数,传递给 append 函数。

除了在切片的尾部追加,我们还可以在切片的开头添加元素:

var a = []int{1,2,3}
a = append([]int{0}, a...)        // 在开头添加 1 个元素
a = append([]int{-3,-2,-1}, a...) // 在开头添加 1 个切片

在开头一般都会导致内存的重新分配,而且会导致已有的元素全部复制 1 次。因此,从切片的开头添加元素的性能一般要比从尾部追加元素的性能差很多。

3.1.2 copy

copy函数可以用来复制切片的内容:

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

可以用 copy 和 append 组合可以避免创建中间的临时切片,同样是完成添加元素的操作:

a = append(a, 0)     // 切片扩展 1 个空间
copy(a[i+1:], a[i:]) // a[i:] 向后移动 1 个位置
a[i] = x             // 设置新添加的元素
  • 第一句 append 用于扩展切片的长度,为要插入的元素留出空间
  • 第二句 copy 操作将要插入位置开始之后的元素向后挪动一个位置
  • 第三句真实地将新添加的元素赋值到对应的位置

用 copy 和 append 组合也可以实现在中间位置插入多个元素(也就是插入一个切片):

用 copy 和 append 组合也可以实现在中间位置插入多个元素(也就是插入一个切片):

a = append(a, x...)       // 为 x 切片扩展足够的空间
copy(a[i+len(x):], a[i:]) // a[i:] 向后移动 len(x) 个位置
copy(a[i:], x)            // 复制新添加的切片

稍显不足的是,在第一句扩展切片容量的时候,扩展空间部分的元素复制是没有必要的。没有专门的内置函数用于扩展切片的容量,append 本质是用于追加元素而不是扩展容量,扩展切片容量只是 append 的一个副作用。

3.2 删除切片元素

根据要删除元素的位置有三种情况:从开头位置删除,从中间位置删除,从尾部删除。

其中删除切片尾部的元素最快:

a = []int{1, 2, 3}
a = a[:len(a)-1]   // 删除尾部 1 个元素
a = a[:len(a)-N]   // 删除尾部 N 个元素

删除开头的元素可以直接移动数据指针:

a = []int{1, 2, 3}
a = a[1:] // 删除开头 1 个元素
a = a[N:] // 删除开头 N 个元素

删除开头的元素也可以不移动数据指针,但是将后面的数据向开头移动。可以用 append 原地完成(所谓原地完成是指在原有的切片数据对应的内存区间内完成,不会导致内存空间结构的变化):

a = []int{1, 2, 3}
a = append(a[:0], a[1:]...) // 删除开头 1 个元素
a = append(a[:0], a[N:]...) // 删除开头 N 个元素

这篇关于【golang学习之旅】复杂数据类型——切片(slice)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1106903

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

线性代数|机器学习-P36在图中找聚类

文章目录 1. 常见图结构2. 谱聚类 感觉后面几节课的内容跨越太大,需要补充太多的知识点,教授讲得内容跨越较大,一般一节课的内容是书本上的一章节内容,所以看视频比较吃力,需要先预习课本内容后才能够很好的理解教授讲解的知识点。 1. 常见图结构 假设我们有如下图结构: Adjacency Matrix:行和列表示的是节点的位置,A[i,j]表示的第 i 个节点和第 j 个

Node.js学习记录(二)

目录 一、express 1、初识express 2、安装express 3、创建并启动web服务器 4、监听 GET&POST 请求、响应内容给客户端 5、获取URL中携带的查询参数 6、获取URL中动态参数 7、静态资源托管 二、工具nodemon 三、express路由 1、express中路由 2、路由的匹配 3、路由模块化 4、路由模块添加前缀 四、中间件