Go语言切片前或中间插入项与内置copy()函数详解


Posted in Golang onApril 27, 2021

内置append()函数能够在切片末尾位置添加新的项,假设要在切片的前面或者中间某位置插入特定项,可以这样实现

看下代码:

package main
import "fmt"
func main() {
    s := []string{"M","N","O","P","Q","R"}
    x := InsertStringSliceCopy(s,[]string{"a","b","c"},0)
    y := InsertStringSliceCopy(s,[]string{"a","b","c"},3)
    fmt.Printf("%v\n%v\n",x,y)
}
func InsertStringSliceCopy(slice,insertion []string,index int)[]string  {
    result := make([]string,len(slice) + len(insertion))
    at := copy(result,slice[:index])
    at += copy(result[at:],insertion)
    copy(result[at:],slice[index:])
    fmt.Printf("%6T\n",at)
    return result
}

运行结果:

Go语言切片前或中间插入项与内置copy()函数详解

自定义的InsertStringSliceCopy()函数可以实现在切片相应的位置插入项

此外InsertStringSliceCopy()函数中打印类变量at的类型,可知内置函数copy()在实现复制功能的时候会有一个int的返回值

补充:go学习备忘录 - 切片中间插入元素

1. 通过链式append 实现

将多个append操作组合起来,实现在切片中间插入元素:

var a []int
a = append(a[:i], append([]int{1}, a[i:]...)...)     // 在第i个位置插入1
a = append(a[:i], append([]int{1,2,3}, a[i:]...)...) // 在第i个位置插入切片

每个链式操作中的第二个append调用都会创建一个临时切片,并将a[i:]的内容复制到新创建的切片中,然后将临时创建的切片再追加到a[:i]。

2. 通过copy + append 实现

通过 copy和append组合 可以避免创建中间的临时切片

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

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

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

注:append本质是用于追加元素而不是扩展容量,扩展切片容量只是append的一个副作用。

补充:Go语言中切片作为函数参数,函数中使用append添加元素

切片作为函数,通过append添加元素,有可能会更改地址:

1)添加的数据元素长度超过切片参数的容量,则会另开辟空间,重新分配底层数组,并复制数据。函数中的此切片与原切片地址不同; 此切片指向新开辟的内存。函数运行结束,内存释放,不会影响元切片的内容。

2)否则原切片与函数中的切片指向同一地址。会影响切片的内容。

3)切片名本身就是一个指针(内容保存指向切片的首地址)

代码测试:

package main
import "fmt"
func main01() {
 s := make([]int, 3, 5)
 s[2] = 8888
 fmt.Printf("原地址:%p", s)
 s = append(s, 12)
 fmt.Printf("\n添加数据之后的地址:%p", s)
 /*
 append添加元素,容量足够,则在原基础之上添加数据,地址不会发生改变
 输出:
 原地址:0xc04207e030
 添加数据之后的地址:0xc04207e030
 */
}
func main02() {
 s := make([]int, 3)
 s[2] = 666
 fmt.Printf("append添加数据之前的地址:%p", s)
 s = append(s, 888)
 fmt.Printf("\nappend添加数据之后的地址:%p", s)
 
 /*
 append添加数据,容量不够,则另行开辟空间,切片地址发生变化
 输出:
 append添加数据之前的地址:0xc04200e2c0
 append添加数据之后的地址:0xc04200a2d0
 */
}
func main() {
 /*
 copy(目的切片,原切片):切片拷贝
 注意事项:目的切片要有足够的空间,如果没有空间(切片为空或者指向0x0),不能进行拷贝
           若目的切片容量不足,只拷贝部分(目的切片长度的部分)
 返回值为拷贝成功的切片数量
 */
 s := make([]int, 3)
 s[0] = 0
 s[1] = 111
 s[2] = 666
 //var s1 []int = []int{5: 333}
 //n:=copy(s,s1)
 s1 := make([]int, 1, 2)
 n := copy(s1, s)
 
 fmt.Printf("原切片s的地址是:%p", s)
 fmt.Printf("\n拷贝之后的切片s1的地址是:%p,数量:%d", s1, n)
 fmt.Println(s1)
}

补充说明:

数组和slice之间有着紧密的联系。一个slice是一个轻量级的数据结构,提供了访问数组子序列(或者全部)元素的功能,而且slice的底层确实引用一个数组对象。一个slice由三个部分构成:指针、长度和容量。指针指向第一个slice元素对应的底层数组元素的地址,要注意的是slice的第一个元素并不一定就是数组的第一个元素。

切片并不是数组或数组指针,它通过内部指针和相关属性引⽤数组⽚段,以实现变⻓⽅案。

slice并不是真正意义上的动态数组,而是一个引用类型。slice总是指向一个底层array,slice的声明也可以像array一样,只是不需要长度。

Go语言切片前或中间插入项与内置copy()函数详解

以上为个人经验,希望能给大家一个参考,也希望大家多多支持三水点靠木。如有错误或未考虑完全的地方,望不吝赐教。

Golang 相关文章推荐
浅谈Golang 嵌套 interface 的赋值问题
Apr 29 Golang
golang slice元素去重操作
Apr 30 Golang
Go标准容器之Ring的使用说明
May 05 Golang
基于Golang 高并发问题的解决方案
May 08 Golang
Golang实现AES对称加密的过程详解
May 20 Golang
Go 语言结构实例分析
Jul 04 Golang
golang实现一个简单的websocket聊天室功能
Oct 05 Golang
Golang中channel的原理解读(推荐)
Oct 16 Golang
golang三种设计模式之简单工厂、方法工厂和抽象工厂
Apr 10 Golang
golang操作redis的客户端包有多个比如redigo、go-redis
Apr 14 Golang
Go web入门Go pongo2模板引擎
May 20 Golang
Go gorilla securecookie库的安装使用详解
Aug 14 Golang
golang中切片copy复制和等号复制的区别介绍
Apr 27 #Golang
go语言中切片与内存复制 memcpy 的实现操作
Apr 27 #Golang
Go语言中的UTF-8实现
Apr 26 #Golang
golang中实现给gif、png、jpeg图片添加文字水印
Apr 26 #Golang
Go语言带缓冲的通道实现
Apr 26 #Golang
go语言求任意类型切片的长度操作
Apr 26 #Golang
golang如何去除多余空白字符(含制表符)
Apr 25 #Golang
You might like
PHP JSON格式数据交互实例代码详解
2011/01/13 PHP
php实现文件下载更能介绍
2012/11/23 PHP
值得分享的php+ajax实时聊天室
2016/07/20 PHP
Yii框架参数化查询中IN查询只能查询一个的解决方法
2017/05/20 PHP
PHP实现基于面向对象的mysqli扩展库增删改查操作工具类
2017/07/18 PHP
设为首页加入收藏兼容360/火狐/谷歌/IE等主流浏览器的代码
2013/03/26 Javascript
单元选择合并变色示例代码
2014/05/26 Javascript
解决jQuery ajax请求在IE6中莫名中断的问题
2016/06/20 Javascript
全面总结Javascript对数组对象的各种操作
2017/01/22 Javascript
微信小程序中的swiper组件详解
2017/04/14 Javascript
在原生不支持的旧环境中添加兼容的Object.keys实现方法
2017/09/11 Javascript
微信小程序日期时间选择器使用方法
2018/02/01 Javascript
详解vue-template-admin三级路由无法缓存的解决方案
2020/03/10 Javascript
微信h5静默和非静默授权获取用户openId的方法和步骤
2020/06/08 Javascript
Python去掉字符串中空格的方法
2014/03/11 Python
Python是编译运行的验证方法
2015/01/30 Python
Python使用functools实现注解同步方法
2018/02/06 Python
Ubuntu下使用Python实现游戏制作中的切分图片功能
2018/03/30 Python
Django配置celery(非djcelery)执行异步任务和定时任务
2018/07/16 Python
python实现ID3决策树算法
2018/08/29 Python
解决python3 Pycharm上连接数据库时报错的问题
2018/12/03 Python
python 将大文件切分为多个小文件的实例
2019/01/14 Python
python 利用百度API识别图片文字(多线程版)
2020/12/14 Python
详解Java中一维、二维数组在内存中的结构
2021/02/11 Python
探索HTML5本地存储功能运用技巧
2016/03/02 HTML / CSS
浅谈html5之sse服务器发送事件EventSource介绍
2017/08/28 HTML / CSS
全球速卖通巴西站点:Aliexpress巴西
2016/08/24 全球购物
2014年基层党组织公开承诺书
2014/03/29 职场文书
社会实践的活动方案
2014/08/22 职场文书
2015年全国助残日活动方案
2015/05/04 职场文书
户外拓展训练感想
2015/08/07 职场文书
煤矿施工安全协议书
2016/03/22 职场文书
canvas多重阴影发光效果实现
2021/04/20 Javascript
Go标准容器之Ring的使用说明
2021/05/05 Golang
Java数据结构之链表相关知识总结
2021/06/18 Java/Android
教你win10系统中APPCRASH事件问题解决方法
2022/07/15 数码科技