首页 > 其他分享 >go slice使用

go slice使用

时间:2023-04-27 10:22:49浏览次数:42  
标签:slice 元素 切片 数组 使用 go sliceName append

1. 简介

在go中,slice是一种动态数组类型,其底层实现中使用了数组。slice有以下特点:

*slice本身并不是数组,它只是一个引用类型,包含了一个指向底层数组的指针,以及长度和容量。
*slice的长度可以动态扩展或缩减,通过appendcopy操作可以增加或删除slice中的元素。
*slice的容量是指在底层数组中slice可以继续扩展的长度,容量可以通过make函数进行设置。

Slice 的底层实现是一个包含了三个字段的结构体:

type`slice`struct {
    ptr uintptr // 指向底层数组的指针
    len int     // slice 的长度
    cap int     // slice 的容量
}

当一个新的slice被创建时,Go会为其分配一个底层数组,并且把指向该数组的指针、长度和容量信息存储在slice结构体中。底层数组的长度一般会比slice的容量要大,以便在append操作时有足够的空间存储新元素。

当一个slice作为参数传递给函数时,其实是传递了一个指向底层数组的指针,这也就意味着在函数内部对slice的修改也会反映到函数外部。

在进行切片操作时,slice 的指针和长度信息不会发生变化,只有容量信息会发生变化。如果切片操作的结果仍然是一个 slice,那么它所引用的底层数组仍然和原来的slice是同一个数组。

需要注意的是,当一个slice被传递给一个新的变量或者作为参数传递给函数时,并不会复制底层数组,而是会共享底层数组。因此,如果对一个slice的元素进行修改,可能会影响到共享底层数组的其他slice。如果需要复制一个slice,可以使用copy函数。

2. 使用

slice的使用包括定义初始化添加删除查找等操作。

2.1 slice定义

slice是一个引用类型,可以通过声明变量并使用make()函数来创建一个slice

var sliceName []T
sliceName := make([]T, length, capacity)

其中,T代表该切片可以保存的元素类型,length代表预留的元素数量,capacity代表预分配的存储空间。

2.2 初始化

slice有两种初始化的方式:声明时初始化和使用append()函数初始化:

// 声明时初始化
sliceName := []T{value1, value2, ..., valueN}

// 使用append()函数进行初始化
sliceName := make([]T, 0, capacity)
sliceName = append(sliceName, value1, value2, ..., valueN)

2.3 获取slice元素

slice中的元素可以通过索引的方式来获取,与c/c++类似,go的索引也是从0开始的:

sliceName[index]

2.4 添加元素到slice中

可以通过使用append()函数将元素添加到slice中。如果slice的容量不足,则会自动扩展。语法如下:

sliceName = append(sliceName, value1, value2, ..., valueN)

2.5 删除slice中的元素

可以使用append()函数和切片操作来从slice中删除元素。使用append()函数时,需要将带有要删除元素的切片放在最后。语法如下:

// 通过切片操作删除元素
sliceName = append(sliceName[:index], sliceName[index+1:]...)

// 通过append()函数删除元素
sliceName = append(sliceName[:index], sliceName[index+1:]...)

如上所见,二者的表现形式是一样的,但内部实现是不同的:

  • 使用append()进行删除的方式,实际上是将后面的元素向前移动一个位置,然后通过重新切片的方式来删除最后一个元素。这种方式会创建一个新的底层数组,并将原来的元素复制到新的数组中,因此在删除多个元素时可能会导致内存分配和复制开销较大,影响性能
  • 使用切片语法进行删除,底层数组中被删除元素的位置仍然存在,但是这些位置不再包含有效的数据。这种方式的性能比使用append()进行删除要好,尤其是在删除多个元素时,因为它不需要创建新的底层数组,也不需要复制元素。但是,这种方式可能会导致底层数组中存在大量未使用的空间,浪费内存

需要注意的是,在切片中删除元素时,会重新分配内存并复制元素,因此删除元素的成本会相对较高。为了减少内存分配和复制元素的次数,可以使用copy函数将后面的元素复制到前面,然后将切片的长度减少。具体实现方法可以参考下面的:

// 删除切片中指定位置的元素
func removeElement(slice []int, index int) []int {
    copy(slice[index:], slice[index+1:])
    return slice[:len(slice)-1]
}

2.6 查找slice中的元素

可以使用forrange遍历slice来实现元素查询:

// 使用for循环和range关键字遍历Slice
for index, value := range sliceName {
    if value == targetValue {
        // 找到了目标元素
        break
    }
}

2.7 切片操作

可以使用切片操作来获取子切片,操作如下:

// 切片操作:获取从第i个元素到第j个元素的子切片
sliceName[i:j]

// 切片操作:获取从第i个元素到第j个元素,且容量为k的子切片
sliceName[i:j:k]

3. 关于slice扩容

在Go语言中,slice会随着元素的增加而动态扩容。当容量不足时,slice会自动重新分配内存,将原有元素复制到新的底层数组中,并在新数组后面添加新的元素。

slice的扩容机制可以描述为:当slice的长度超过了底层数组的容量时,Go语言会按照一定的策略重新分配一块更大的内存,并将原来的元素复制到新的内存中,然后再添加新元素。具体的策略如下:

  1. 如果新长度(即len(s)+1)小于等于原长度(即cap(s)),则slice不需要扩容,直接添加元素即可。
  2. 如果新长度大于原长度且小于原长度的两倍(即 cap(s)*2),则新slice的容量就是原来的两倍,也就是说将底层数组扩容为原来的两倍,并将原来的元素复制到新的数组中。
  3. 如果新长度大于原长度的两倍,会尝试使用新长度作为容量,如果仍然不够,则按照扩容倍数(默认是 2)来扩容。

需要注意的是,slice扩容是一个开销比较大的操作,因为需要重新分配内存、复制数据等。所以在编写代码时应该尽可能地减少slice扩容的次数,以提高程序的性能。


声明:本作品采用署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)进行许可,使用时请注明出处。
Author: mengbin
blog: mengbin
Github: mengbin92
cnblogs: 恋水无意


标签:slice,元素,切片,数组,使用,go,sliceName,append
From: https://www.cnblogs.com/lianshuiwuyi/p/17358192.html

相关文章

  • python yaml 配置文件的使用
    yaml介绍yaml的英文名称是YetAnotherMarkupLanguage,直译过来就是标记语言。yaml用的比较多的地方就是写配置文件,比如yaml配置文件在Kubernetes用的就非常广泛,学Kubernetes必须要先学yaml文件格式。yaml的基本语法:大小写敏感使用缩进表示层级关系缩进时不允许使用Tab......
  • 使用Windows API获取文件,或者文件夹的大小
    获取一个文件的大小:通过使用GetFileSizeEx获取文件的大小,LONGLONGFileSize(CStringfile)constnoexcept{HANDLEfp=CreateFile(file.GetBuffer(),GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);LARGE_INTEGERinfo;memset(&info,......
  • word使用技巧总结(不定时更新)
    注:本人使用的是WPS。1.两个word文档怎么并排显示窗口? “视图”--“并排显示”,可点“同步滚动”或者“重设位置”;2.保留文档修改痕迹?“审阅”--“修订”,可设置用户名等修订选项,可逐个点“接受”、“拒绝”,也可一次性全部“接受”或者“拒绝”。......
  • python+playwright 学习-58 Jenkins上使用 Pipeline 运行 playwright 自动化脚本
    前言Dockerfile.focal可用于在Docker环境中运行Playwright脚本。这些镜像包括在Docker容器中运行浏览器所需的所有依赖项,还包括浏览器本身。playwright在linux系统上目前只支持Ubuntu系统的部分版本,centos和debian系统上是没法运行的。jenkins环境准备需安装2个跟d......
  • 强引用、软引用、弱引用、幻象引用有什么区别?具体使用场景是什么?
    典型回答不同的引用类型,主要体现的是对象不同的可达性(reachable)状态和对垃圾收集的影响。所谓强引用(“Strong”Reference),就是我们最常见的普通对象引用,只要还有强引用指向一个对象,就能表明对象还“活着”,垃圾收集器不会碰这种对象。对于一个普通的对象,如果没有其他的引用关系,只......
  • 【单例设计模式原理详解】Java/JS/Go/Python/TS不同语言实现
    简介单例模式(SingletonPattern)属于创建型设计模式,这种模式只创建一个单一的类,保证一个类只有一个实例,并提供一个访问该实例的全局节点。当您想控制实例数目,节省系统资源,并不想混用的时候,可以使用单例模式。单例有很多种实现方式,主要分为懒汉和饿汉模式,同时要通过加锁来避免线程......
  • 使用axios下载文件
    使用axios下载文件/***下载文件*@param{string}url下载地址*@param{string}fileName文件名,例:1.png*/exportfunctiondownload(url,fileName){axios({url:url,method:'GET',responseType:'blob',}).then((r......
  • 使用youtube-dl和yt-dlp下载视频!
    导语我们在某网站上观看视频时,经常会碰到以下的尴尬情况,不是被删了就是被设为私有了,十分烦人.其实我们可以通过youtube-dl和yt-dlp将Youxxbe上的视频下载到自己电脑上,以防万一.本文将介绍如何使用这两款工具下载视频.youtube-dl由于版权问题,youtube-dl的下载速度......
  • Go Web学习笔记--处理表单的输入
    通过一个注册的示例来演示如何通过Go语言来处理表单的输入。首先,创建一个简单的html文件,代码如下:<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>Title</title></head><body><formaction="/log......
  • Java8使用Stream API转换Map遇到的2种异常报错和解决思路
    问题java8提供了StreamAPI,配合Lambda表达式,让开发者能对集合对象进行便利、高效的操作。在日常业务开发中,有个经常用到的场景是将List类型对象转换为Map类型对象,方便后续操作。在java8之前,这种转换需要先new一个Map对象,遍历list然后通过Map#put来初始化。使用java8后,可方便的......