Golang:slice之append时原数组发生变化的问题

使用append可以在slice之后追求元素,例如

nums:=[]int{1,2,3}
result:=append(nums,4)
fmt.Println(result)

这段代码很简单,输出result的值为:[1 2 3 4]

问题在于,进行这种操作时,原来的slice(即nums)所基于的数组的值会不会发生变化呢?在Golang中,如果有多个slice基于了同一个数组,则这些slice的数据是共享的(而不是每个slice复制一份)。也就说,如果改变了数组的内容,则基于它的所有slice的值都会变化这段代码中nums的值没有变化,但是并非所有时候都是如此。

回答这个问题,首先需要了解append函数实现原理:

1. 如果nums的cap够用,则会直接在nums指向的数组后面追加元素,返回的slice和原来的slice是同一个对象。显然,这种情况下原来的slice的值会发生变化!

2. 如果nums的cap不够用(上述代码就是这种情况),则会重新分配一个数组空间用来存储数据,并且返回指向新数组的slice。这时候原来的slice指向的数组并没有发生任何变化!

3. 当然,在任何情况下,返回的结果都是追加之后的slice,这一点没有问题!

以下代码用来验证这个问题:

(1)在函数test1nums的值发生变化了,因为nums[:2]的len为2,cap为3,所以追加一个元素时cap依然够用;

(2)在函数test2nums的值没有发生变化,因为nums[:2]的cap不够用,因此会重新分配一个数组用来存储新的数据,而nums存储的仍然是老数组。

func test1() {
    nums := []int{1, 2, 3}
    _ = append(nums[:2], 4)
    fmt.Println("test1:", nums)

    //nums changes because the cap is big enought, the original array is modified.

}

func test2() {
    nums := []int{1, 2, 3}
    c := append(nums[:2], []int{4, 5, 6}...)
    fmt.Println("test2:", nums)
    fmt.Println("cc:", c)

    //nums dont‘t change because the cap isn‘t big enought.
    //a new array is allocated while the nums still points to the old array.
    //Of course, the return value of append points to the new array.
}

<完结>

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-02 11:12:47

Golang:slice之append时原数组发生变化的问题的相关文章

Golang Slice 总结

数组 Go的切片是在数组之上的抽象数据类型,因此在了解切片之前必须要要理解数组.数组类型由指定和长度和元素类型定义.数组不需要显式的初始化:数组元素会自动初始化为零值:Go的数组是值语义.一个数组变量表示整个数组,它不是指向第一个元素的指针(比如C语言的数组).当一个数组变量被赋值或者被传递的时候,实际上会复制整个数组.(为了避免复制数组,你可以传递一个指向数组的指针,但是数组指针并不是数组.)可以将数组看作一个特殊的struct,结构的字段名对应数组的索引,同时成员的数目固定. 切片 数组虽然

【转】 golang slice array

1. array   同一类型数据的集合     var arr [n]type    //声明type类型一维数组     var arr [m][n]type //声明type类型二维数组     多维数组以此类推     也可以用 := 声明     arr := [n]type{元素1[,元素2, ...]} 其中n可以用 "..." 三个点表示,系统会根据元素个数来确定   下标只能为 int 类型,而 php 还支持 string 类型的下标   1.1 数组长度 len(

golang slice性能分析

golang在gc这块的做得比较弱,频繁地申请和释放内存会消耗很多的资源.另外slice使用数组实现,有一个容量和长度的问题,当slice的容量用完再继续添加元素时需要扩容,而这个扩容会把申请新的空间,把老的内容复制到新的空间,这是一个非常耗时的操作.有两种方式可以减少这个问题带来的性能开销: 在slice初始化的时候设置capacity(但更多的时候我们可能并不知道capacity的大小) 复用slice 下面就针对这两个优化设计了如下的benchmark,代码在: https://githu

js中改变原数组的方法以及解决改变原数组的方法

改变原数组的方法 pop():删除数组最后一个元素,并返回该元素 push():在数组尾部添加元素,并返回更新后的数组长度 shift():删除数组的第一个元素,并返回该元素 unshift():在数组第一位添加元素,并返回更新后的数组长度 sort():对数组排序(按字符ASCII进行排序),也可添加回调函数按照想要的规则排序 reverse():数组反转 splice(index, howmany, 新数据):返回被删除元素所组成的数组. 解决改变原数组的方法 背景:需要一个和原数组相等的数

JavaScript中常见数组方法原数组改不改变

改变原数组 1 pop():删除 arrayObject 的最后一个元素,把数组长度减 1,并且返回它删除的元素的值.如果数组已经为空,则 pop() 不 改变数组,并返回 undefined 值.arrayObject.pop() 2 push(): push() 方法可把它的参数顺序添加到 arrayObject 的尾部. 它直接修改 arrayObject,而不是创建一个新的数组,arrayObject.push(newelement1,newelement2,….,newelementX

Golang Slice切片

1. 切片的定义和初始化 切片是基于数组类型的封装,非常灵活,可以自动扩容 切片是真正意义上的动态数组,而且是一个引用类型,切片指向一个底层数组 定义切片 var 切片名[] 切片类型 创建切片方式 package main import "fmt" func main() { // 1. 声明切片 var s1 []int if s1 == nil { // 只声明没有初始化,所以是nil fmt.Println("为空") } else { fmt.Printl

观V8源码中的array.js,解析 Array.prototype.slice为什么能将类数组对象转为真正的数组?

在官方的解释中,如[mdn] The slice() method returns a shallow copy of a portion of an array into a new array object. 简单的说就是根据参数,返回数组的一部分的copy.所以了解其内部实现才能确定它是如何工作的.所以查看V8源码中的Array.js     可以看到如下的代码: 一.方法  ArraySlice,源码地址,直接添加到Array.prototype上的"入口",内部经过参数.类型

是否改变原数组的常用方法归纳

改变原数组: 1.pop()  删除数组最后一个元素,如果数组为空,则不改变数组,返回undefined,改变原数组,返回被删除的元素 2.push()   向数组末尾添加一个或多个元素,改变原数组,返回新数组的长度 3.reverse()   颠倒数组中元素的顺序,改变原数组,返回该数组 4.shift()   把数组的第一个元素删除,若空数组,不进行任何操作,返回undefined,改变原数组,返回第一个元素的值 5.sort()   对数组元素进行排序,改变原数组,返回该数组 6.spli

task 2:移出数组arr中与2相等的元素,并生成一个新数组,不改变原数组。

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>移出数组arr中与2相等的元素,并生成一个新数组,不改变原数组.</title> </head> <body> <script> var arr = [1,2,3,4,2,5,6,2,7,2]; var str4 = arr.slice(1,2); var st