1. 简介

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

*slice本身并不是数组,它只是一个引用类型,包含了一个指向底层数组的指针,以及长度和容量。

*slice的长度可以动态扩展或缩减,通过append和copy操作可以增加或删除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中的元素

可以使用for和range遍历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: 恋水无意


go slice使用的更多相关文章

  1. Matlab slice方法和包络法绘制三维立体图

    前言:在地球物理勘探,流体空间分布等多种场景中,定位空间点P(x,y,x)的物理属性值Q,并绘制三维空间分布图,对我们洞察空间场景有十分重要的意义. 1. 三维立体图的基本要件: 全空间网格化 网格节 ...

  2. jQuery之常用且重要方法梳理(target,arguments,slice,substring,data,trigger,Attr)-(一)

    1.jquery  data(name) data() 方法向被选元素附加数据,或者从被选元素获取数据. $("#btn1").click(function(){ $(" ...

  3. js url.slice(star,end) url.lastIndexOf('/') + 1, -4

    var url = '"http://60.195.252.25:15518/20151228/XXSX/作三角形的高.mp4")' document.title = url.sl ...

  4. JavaScript中的slice,splice,substr,substring,split的区别

    万恶的输入法,在sublime中会显示出繁体字,各位看官见谅. 1.slice()方法:该方法在数组和string对象中都拥有. var a = [1,2,3,4,5,6]; var s = 'thi ...

  5. Max double slice sum 的解法

    1. 上题目: Task description A non-empty zero-indexed array A consisting of N integers is given. A tripl ...

  6. js中substr,substring,slice。截取字符串的区别

    substr(n1,n2) n1:起始位置(可以为负数) n2:截取长度(不可以为0,不可以为负数,可以为空) 当n1为正数时,从字符串的n1下标处截取字符串(起始位置),长度为n2. 当n1为负数时 ...

  7. JS 中 Array.slice() 和 Array.splice()方法

    slice slice()就是对应String的substring()版本,它截取Array的部分元素,然后返回一个新的Array: var arr = ['A', 'B', 'C', 'D', 'E ...

  8. 【javascript 技巧】Array.prototype.slice的妙用

    Array.prototype.slice的妙用 开门见山,关于Array 的slice的用法可以参考这里 http://www.w3school.com.cn/js/jsref_slice_arra ...

  9. golang中的slice翻转存在以及map中的key判断

    //slice翻转 func stringReverse(src []string){ if src == nil { panic(fmt.Errorf("the src can't be ...

  10. jQuery中slice()用法总结

    <!DOCTYPE html> <html> <head lang="en"> <meta charset="utf-8&quo ...

随机推荐

  1. shortcuts

    关闭选项卡 Ctrl+W 关闭当前窗口 alt + F4 alt + 空格 + c alt + 空格 + n 最小化窗口 alt + 空格 + x 最大化窗口 ALT+F4 关闭当前应用程序 ctrl ...

  2. vue3.0的生命周期函数

    stetup(){}在 生命周期函数 系列中的优先级 〇setup(){ //优先级最高 处于created生命周期之前的函数,是无法访问data,methods中的数据是无法访问到的,setup中的 ...

  3. MySQL代替in之临时表

    如果我们正常的使用IN去查询 SELECT * FROM a JOIN b ON a.id = b.id WHERE b.tag_id IN (1,2,3,4,5,6) 这种因为in里面的参数是连续的 ...

  4. Servlet的学习之路

    一.什么是什么Servlet? Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程 ...

  5. How to Change Reset Retrieve the WebLogic Server Administrator Password on WLS 10.3.6 or earlier

    To change the Administrator password on WLS 10.3.6 or earlier, perform the following steps depending ...

  6. linux 数据卷磁盘管理

    新加磁盘如何创建逻辑分区 查看命令 lvs  vgs pvs 1: 新建一个分区(partition)fdisk /dev/sdb 2:创建PV(物理卷)pvcreate /dev/sdb1pvs或 ...

  7. [代码片段] 获取分辨率DPI和像素、毫米、英寸互相转换

    private static float DEFAULT_DPI_X = 0; private static float DEFAULT_DPI_Y = 0; /// <summary>获 ...

  8. C#处理JSON类型数据序列化和反序列化的一点心得体会

    在处理JSON类型的数据时,定义了很多JSON类型.经常需要用到序列化和反序列化.刚开始接触到这个问题时,我给每个JSON类型都增加了类似下方的代码. using System; using Syst ...

  9. sql自学记录

    复习 SQL join :用于根据两个或多个表中的列之间的关系,从这些表中查询数据 eg: SELECT Persons.LastName, Persons.FirstName, Orders.Ord ...

  10. strtok实现想到的...

    1.实现容易,实现的方法很难想到 比如strtok函数 /* 获取第一个子字符串 */ token = strtok(str, s); /* 继续获取其他的子字符串 */ while( token ! ...