Go slice切片make生成append追加copy复制示例

目录
  • 回顾
  • 加深理解
  • 生成切片 make
  • 更好的理解长度和容量
  • 切片引用类型实战
    • 切片的遍历
  • append
    • 多次追加
    • 追加多个元素
  • 复制切片
  • 删除元素
  • 数组转切片
  • 实战演练
  • 总结

回顾

上一篇文章我们介绍了切片slice的定义初始化、引用类型特征、如何使用数组切割成切片。

这篇文章介绍切片的生成make()、切片的追加append()、切片的复制copy()。对知识点进行详细介绍和应用实战。

加深理解

  • 切片的本质:切片的本质是一个框,框住了一块连续的内存
  • 切片属于引用类型,真正的数据都是保存在底层数组里的
  • 切片可以简单理解为是快捷方式,修改会互相影响
  • 判断一个切片是否为空,使用len(s) == 0 判断,不能使用 s==nil 判断

生成切片 make

上需求:请定义一个长度为5,容量为10的整型切片。

上代码:

s1 := make([]int,5,10)
fmt.Printf("s1:%v len(s1):%d cap(s1):%d\n", s1, len(s1), cap(s1))

打印结果:

分析:make()函数的第一个参数指定切片的数组类型,第二个参数指定切片的长度,第三个参数指定切片的容量。

更好的理解长度和容量

s1 := make([]int,5,10)
fmt.Printf("s1:%v len(s1):%d cap(s1):%d\n", s1, len(s1), cap(s1))
s2 := make([]int, 0, 10)
fmt.Printf("s2=%v len(s2)=%d cap(s2)=%d\n", s2, len(s2), cap(s2))

打印结果:

分析: 我们可以发现定义切片时元素的个数和长度相关,因为长度就是元素的个数。

容量我们在下面介绍append()时,重点介绍一下。

切片引用类型实战

上代码

//切片
s3 := make([]int, 3, 3)
s3 = []int{1, 2, 3}
s4 := s3            //s3 s4都指向同一个底层数组
fmt.Println(s3, s4) //[1 2 3]  [1 2 3]
s3[0] = 1000
fmt.Println(s3, s4) //[1000 2 3] [1 2 3]
fmt.Println("-----")
//数组
a3 := [3]int{1, 2, 4}
a4 := a3
fmt.Println(a3, a4)
a3[0] = 1000
fmt.Println(a3, a4)

打印结果:

分析:通过上面的打印结果我们可以很直观的看出来,切片引用类型的本质,当切片修改时会互相影响;而数组作为值类型,不会互相影响。

切片的遍历

和数组一样,用fori或者for range进行遍历即可。

s3 := make([]int, 3, 3)
s3 = []int{1, 2, 3}
for i := 0; i < len(s3); i++ {
   fmt.Println(s3[i])
}
for i, v := range s3 {
   fmt.Println(i, v)
}

append

首先,我们定义一个切片

s1 := []string{"北京", "上海", "大连", "佛山"}
fmt.Printf("s1=%v len(s1)=%d cap(s1)=%d\n", s1, len(s1), cap(s1))

打印结果:

分析:我们发现切片的长度和容量都是4

然后,我们使用append()函数追加一个元素

s1 := []string{"北京", "上海", "大连", "佛山"}
fmt.Printf("s1=%v len(s1)=%d cap(s1)=%d\n", s1, len(s1), cap(s1))
s1 = append(s1, "唐山") //切片append()追加之后,
fmt.Printf("s1=%v len(s1)=%d cap(s1)=%d\n", s1, len(s1), cap(s1))

打印结果:

分析:长度由4变成5,我们很好理解;容量为什么会从4变成8呢?

这是Go语言对切片的自动扩容机制。append()追加元素,原来的底层数据容量不够时,go底层会把底层数组替换,是go语言的一套扩容策略

我后面会单独写一篇来讲自动扩容策略是如何实现的。欢迎大家持续关注我的专栏Go语言学习专栏

多次追加

多次追加的概念很好理解,就是多次调用append()

举个栗子:

s1 := []string{"北京", "上海", "大连", "佛山"}
fmt.Printf("s1=%v len(s1)=%d cap(s1)=%d\n", s1, len(s1), cap(s1))
s1 = append(s1, "唐山") //切片append()追加之后,
fmt.Printf("s1=%v len(s1)=%d cap(s1)=%d\n", s1, len(s1), cap(s1))
var s2 []string
s2 = append(s1, "雄安")
fmt.Printf("s2=%v len(s2)=%d cap(s2)=%d\n", s2, len(s2), cap(s2))

打印结果:

分析: s1经过两次append追加元素,赋值给了s2

追加多个元素

当我们需要追加多个元素时,难道只能像上面这样多次调用append吗?

当然不是的。

举个栗子:

s1 := []string{"北京", "上海", "大连", "佛山"}
s3 := []string{"太原","石家庄"}
var s4 []string
s4 = append(s1,s3...) // ...表示拆开,将切片的值作为追加的元素
fmt.Printf("s4的值:%v",s4)

打印结果:

注意:append的第二个参数,我们传入了一个切片,需要在切片后写死...,表示将切片切割,将切片的值作为追加到第一个参数中的元素。

复制切片

下面演示两种方式:

//定义切片s1
s1 := []int{1, 2, 3}
//第一种方式:直接声明变量 用=赋值
//s2切片和s1引用同一个内存地址
var s2 = s1
//第二种方式:copy
var s3 = make([]int, 3)
copy(s3, s1)            //使用copy函数将 参数2的元素复制到参数1
fmt.Println(s1, s2, s3) //都是[1 2 3]

打印结果:都返回[1 2 3]

注意:make和:=不能同时使用,这种是错误的写法 :s3 := make([]int, 5)

聪明的小伙伴们是不是提出疑问了呢?

既然结果一样,为什么要引出copy这个函数呢?

咱们接着往下看,就知道所以然了。

//定义切片s1
s1 := []int{1, 2, 3}
//第一种方式:直接声明变量 用=赋值
//s2切片和s1引用同一个内存地址
var s2 = s1
//第二种方式:copy
var s3 = make([]int, 3)
copy(s3, s1)            //使用copy函数将 参数2的元素复制到参数1
s1[0] = 11
fmt.Printf("s1:%v s2:%v s3:%v",s1, s2, s3) //s1和s2是[11 2 3] s3是[1 2 3]

打印结果:

分析: 我们发现s1和s2是[11 2 3] s3是[1 2 3],说明copy方法是复制了一份,开辟了新的内存空间,不再引用s1的内存地址,这就是两者的区别。

删除元素

注意:删除切片中的元素 不能直接删除 可以组合使用分割+append的方式删除切片中的元素

举个栗子:比如切除s3中的元素2(下标为1的元素)

s3 := []int{1, 2, 3}
s3 = append(s3[:1], s3[2:]...) //第一个不用拆开 原因是一个作为被接受的一方  是把后面的元素追加到第一个
fmt.Println(s3)

打印结果:

注意:上面代码段中有我添加的注释:append()函数中第一个切片不用拆开,原因是一个作为被接受的一方,是把后面的元素追加到第一个切片中。

数组转切片

a1 := [...]int{1,2,3}
s1 := a1[:]
fmt.Printf("a1类型:%T\ns1类型:%T",a1,s1)

打印结果:

实战演练

猜想一下下面程序的运行结果:

s1 := make([]int, 5, 10)
for i := 0; i < 10; i++ {
   s1 = append(s1, i)
}
fmt.Println(s1)

.

.

.

打印结果:

分析: 我们静下心来逐步推导就ok了:

  • s1 := make([]int, 5, 10) 生成的是切片: [0 0 0 0 0]
  • for循环中每次将自增的i值追加到上面的切片中
  • 所以最终打印的结果是:[0 0 0 0 0 0 1 2 3 4 5 6 7 8 9]

总结

这篇文章汇总了使用make生成切片、使用append追加切片元素、使用copy复制切片,开辟新的内存空间、使用切片分割和append来删除切片,更多关于Go make生成slice切片的资料请关注我们其它相关文章!

(0)

相关推荐

  • Go语言--切片(Slice)详解

    目录 一.定义切片 1.声明一个未指定大小的数组来定义切片 2.使用make()函数来创建切片 二.切片是可索引的 1.len() 和 cap() 函数 三.切片截取 四.增加切片的容量 说明: Go 语言切片是对数组的抽象. Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大. 一.定义切片 注意:切片不需要说明长度 1.声明一个未指定大小

  • Go slice切片使用示例详解

    目录 定义 定义并初始化 长度和容量 由数组得到切片 更多切割方式举例 切片的长度和容量 切片再切片 slice是引用类型 总结 定义 切片区别于数组,是引用类型, 不是值类型.数组是固定长度的,而切片长度是可变的,我的理解是:切片是对数组一个片段的引用. var s1 []int //定义一个存放int类型元素的切片 var s2 []string //定义一个存放string类型元素的切片 fmt.Println(s1, s2) fmt.Println(s1 == nil) //true 为

  • 浅谈Golang Slice切片如何扩容的实现

    目录 一.Slice数据结构是什么? 二.详细代码 1.数据结构 2.扩容原则 3.如何理解扩容规则一 1.当小于1024个元素时 2.当大于1024个元素时 4.如何理解扩容规则二 1.简单理解内存地址更换 总结 一.Slice数据结构是什么? 切片(slice)是 Golang 中一种比较特殊的数据结构,这种数据结构更便于使用和管理数据集合.切片是围绕动态数组的概念构建的,可以按需自动增长和缩小.切片(slice)是可以看做是一个长度可变的数组.切片(slice)自身并不是动态数组或者数组指

  • Go基础系列:Go切片(分片)slice详解

    slice表示切片(分片),例如对一个数组进行切片,取出数组中的一部分值.在现代编程语言中,slice(切片)几乎成为一种必备特性,它可以从一个数组(列表)中取出任意长度的子数组(列表),为操作数据结构带来非常大的便利性,如python.perl等都支持对数组的slice操作,甚至perl还支持对hash数据结构的slice. 但Go中的slice和这些语言的slice不太一样,前面所说的语言中,slice是一种切片的操作,切片后返回一个新的数据对象.而Go中的slice不仅仅是一种切片动作,还

  • 浅谈Golang 切片(slice)扩容机制的原理

    我们知道 Golang 切片(slice) 在容量不足的情况下会进行扩容,扩容的原理是怎样的呢?是不是每次扩一倍?下面我们结合源码来告诉你答案. 一.源码 Version : go1.15.6  src/runtime/slice.go //go1.15.6 源码 src/runtime/slice.go func growslice(et *_type, old slice, cap int) slice { //省略部分判断代码 //计算扩容部分 //其中,cap : 所需容量,newcap

  • Go slice切片make生成append追加copy复制示例

    目录 回顾 加深理解 生成切片 make 更好的理解长度和容量 切片引用类型实战 切片的遍历 append 多次追加 追加多个元素 复制切片 删除元素 数组转切片 实战演练 总结 回顾 上一篇文章我们介绍了切片slice的定义初始化.引用类型特征.如何使用数组切割成切片. 这篇文章介绍切片的生成make().切片的追加append().切片的复制copy().对知识点进行详细介绍和应用实战. 加深理解 切片的本质:切片的本质是一个框,框住了一块连续的内存 切片属于引用类型,真正的数据都是保存在底

  • Golang slice切片操作之切片的追加、删除、插入等

    本文介绍了Golang slice切片操作之切片的追加.删除.插入等,分享给大家,具体如下: 一.一般操作 1,声明变量,go自动初始化为nil,长度:0,地址:0,nil func main(){ var ss []string; fmt.Printf("length:%v \taddr:%p \tisnil:%v",len(ss),ss, ss==nil) } --- Running... length:0 addr:0x0 isnil:true Success: process

  • 深入解析Go语言编程中slice切片结构

    数组转换成切片 复制代码 代码如下: a := [10]int{} fmt.Println(a)  s1 := a[:10]  //取前10个元素 [5:]取 5-最后的元素  fmt.Println(s1) slice测试 复制代码 代码如下: a := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'h'} sa := a[2:5] fmt.Println(string(sa)) sd1 := a[3:5] fmt.Println(string(sd1)) //看

  • golang中切片copy复制和等号复制的区别介绍

    结论: copy复制会比等号复制慢.但是copy复制为值复制,改变原切片的值不会影响新切片.而等号复制为指针复制,改变原切片或新切片都会对另一个产生影响. 测试复制速度: func TestArr1(t *testing.T) { var a []int for i := 0; i < 100000000; i++ { a = append(a, i) } start := time.Now().UnixNano() var b = make([]int, 1000000) copy(b, a)

  • 对python 生成拼接xml报文的示例详解

    最近临时工作要生成xml报名,通过MQ接口发送.简单小程序. 自增长拼成xml报文 Test_001.py # encoding=utf-8 import time orderId = '' s1= "\n" # for ID in range(1,5): item1 = "<item>" + \ "<orderID>" + str(ID) + "</orderID>" + \ "

  • js生成二维码的示例代码

    前段时间项目中需要开发扫描二维码查看信息的功能,在网上查了一些资料,把用过的方法进行总结需要导入一个qrcode的js 插件. 插件链接: qrcode.js下载地址,点击即可下载 一.一个简单的示例 如下:(仅供参考) <%-- Created by IntelliJ IDEA. User: ASUS author:xumz Date: 2021/2/27 Time: 10:33 搬运请备注 To change this template use File | Settings | File

  • Java随机生成姓名,手机号,住址代码示例

    这篇文章主要是关于Java随机生成姓名,手机号,住址的代码,你要是问我有什么用,我也不知道,纯粹练习吧. 随机生成测试数据 import java.util.HashMap; import java.util.Map; /** * 随机生成中文姓名,性别,Email,手机号,住址 * @author X-rapido */ public class RandomValue { public static String base = "abcdefghijklmnopqrstuvwxyz01234

  • Java中生成唯一ID的方法示例

    有时我们不依赖于数据库中自动递增的字段产生唯一ID,比如多表同一字段需要统一一个唯一ID,这时就需要用程序来生成一个唯一的全局ID. UUID 从Java 5开始, UUID 类提供了一种生成唯一ID的简单方法.UUID是通用唯一识别码 (Universally Unique Identifier)的缩写,UUID来源于OSF(Open Software Foundation,开源软件基金会)的DCE(Distributed Computing Environment,分布式计算环境)规范.UU

随机推荐