Go——切片

1. 特点

        slice并不是数组或数组指针。它通过内部指针和相关属性引用数组片段,以实现变长方案。

  • 切片:切片是数组的一个引用,因此切片是引用类型。但自身是结构体,值拷贝传递。
  • 切片的长度可以改变,因此,切片是一个可变数组。
  • 切片的遍历方式和数组一样,可以用len()求长度。表示可用元素的数量,读写操作不能超过该限制。
  • cap可以求出slice最大扩张容量,不能超出数组限制。0 <= len(slice) <= len(array)。其中array是slice引用的数组。
  • 切片的定义:var 变量名 []类型,比如:var str []string  var arr []int。
  • 如果slice等于nil,那么len,cap结果都等于0。

2. 切片源码

        切片对应的数据结构定义位于runtime/slice.go文件中,其定义如下:

type slice struct {array unsafe.Pointerlen   intcap   int
}
  • array:指针,指向引用数组对应位置。
  • len:可用元素个数。
  • cap:容量,可放元素个数。

        我们在进行切片赋值,传参,截断时,其实是复制一个slice结构体,只不过底层数组是同一个。这就导致了无论是在复制的切片中修改值,还是修改形参切片值,都会修改到原来的切片和引用的数组。

        总的来说:切边底层有一个数组(make创建的切片,底层也会先创建一个数组),切片是数组的引用。

3. 创建切片

4. 切片初始化

package mainimport "fmt"//全局变量
var arr = [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
var slice0 []int = arr[2:8]
var slice1 []int = arr[0:6]            //可以简写为arr[:6]
var slice2 []int = arr[5:10]           //可以简写为arr[5:]
var slice3 []int = arr[0:len(arr)]     //可以简写为arr[:]
var slice4 []int = arr[0 : len(arr)-1] //去掉最后一个元素func main() {fmt.Printf("全局变量arr %v\n", arr)fmt.Printf("全局变量slice0 %v\n", slice0)fmt.Printf("全局变量slice1 %v\n", slice1)fmt.Printf("全局变量slice2 %v\n", slice2)fmt.Printf("全局变量slice3 %v\n", slice3)fmt.Printf("全局变量slice4 %v\n", slice4)fmt.Println("--------------------------")arr2 := [...]int{9, 8, 7, 6, 5, 4, 3, 2, 1, 0}slice5 := arr2[2:8]slice6 := arr2[0:6]         //可以简写为arr[:6]slice7 := arr2[5:10]        //可以简写为arr[5:]slice8 := arr2[0:len(arr)]  //可以简写为arr[:]slice9 := arr2[:len(arr)-1] //去掉最后一个元素fmt.Printf("局部变量arr %v\n", arr2)fmt.Printf("局部变量slice5 %v\n", slice5)fmt.Printf("局部变量slice6 %v\n", slice6)fmt.Printf("局部变量slice7 %v\n", slice7)fmt.Printf("局部变量slice8 %v\n", slice8)fmt.Printf("局部变量slice9 %v\n", slice9)
}

 5. 通过make来创建切片

var slice []type = make([]type, len)
var slice []type = make([]type, len, cap)
slice := make([]type, len)
slice := make([]type, len, cap)

  • 切片的内存布局 

  • 读写操作实际目标是底层数组,只需要注意索引号的差别。
package mainimport "fmt"func main() {data := [...]int{0, 1, 2, 3, 4, 5}s := data[2:4]s[0] += 100s[1] += 200fmt.Println(s)fmt.Println(data)
}

  • 可直接创建slice对象,自动分配底层数组
package mainimport "fmt"func main() {var s1 []int = []int{0, 1, 2, 3, 8: 100} //通过初始化表达式构造,可使用索引号fmt.Println(s1, len(s1), cap(s1))s2 := make([]int, 6, 8) //使用make创建,指定len和capfmt.Println(s2, len(s2), cap(s2))s3 := make([]int, 6) //使用make创建,省略cap,相当于cap=lenfmt.Println(s3, len(s3), cap(s3))
}

  • 使用make动态创建slice,避免了数组必须用常量的麻烦。还可以用指针直接访问底层数组,退化成普通数组操作。 
package mainimport "fmt"func main() {s := []int{1, 2, 3, 4}p := &s[1] //获得底层数组元素指针*p += 100fmt.Println(s)
}

  •  至于[][]T,是指元素类型为[]T
package mainimport "fmt"func main() {data := [][]int{[]int{1, 2, 3},[]int{10, 20, 30, 40},[]int{100, 200},}fmt.Println(data)
}

  • 可以直接修改struct array/slice成员 
package mainimport "fmt"func main() {data := [5]struct {x int}{}s := data[:]s[1].x = 10s[2].x = 20fmt.Println(data)fmt.Printf("%p, %p\n", &data, &data[0])
}

6. 用append内置函数操作切片(切片追加)

        append:向slice尾部添加数据,返回新的slice对象。

package mainimport ("fmt"
)func main() {var s []int //s是nil,没有分配内存fmt.Println(s)s = append(s, 1) //append会先为s分配内存,再存放数据fmt.Println(s)var a []int = []int{1, 2, 3}fmt.Printf("slice a: %v\n", a)b := []int{4, 5, 6}fmt.Printf("slice b: %v\n", b)//append向slice尾部添加数据c := append(a, b...)fmt.Printf("slice c: %v\n", c)d := append(a, 10)fmt.Printf("slice d: %v\n", d)e := append(a, 100, 200, 300)fmt.Printf("slice e: %v\n", e)//append返回的新的slice对象fmt.Printf("a:%p, c:%p, d:%p, e:%p", &a, &c, &d, &e)
}

8. slice扩容

  • 超出元slice.cap限制,就会重新分配底层数组,即便原数组并未填满。  

        从下面现象可以看出: 

  • 当数据没有超出slice的cap时,如果引用存在的数组变量(不是make创建的),底层数组使用的是存在的数组变量。修改切片的值,就会修改数组的值。
  • 当数据超出slice的cap时,会重新分配底层数组,修改切片的值,不会修改存在数组变量的值,而是修改切片底层数组的值。
package mainimport "fmt"func main() {var data = [...]int{1, 2, 3, 4, 10: 0}s := data[:2:3]fmt.Printf("data:%p, data[0]:%p\n", &data, &data[0]) //data:0xc0000103f0, data[0]:0xc0000103f0//slice中数据保存的是数组对应下标的地址,使用索引访问,相当于对指针解引用,在取地址,实际取的是数组中的地址fmt.Printf("s:%p, s[0]:%p\n", &s, &s[0]) //s:0xc000008048, s[0]:0xc0000103f0//未扩容前修改值s[0] = 11fmt.Println(s, data)s = append(s, 100, 200)fmt.Println(s, data)fmt.Printf("s[0]:%p, data[0]:%p\n", &s[0], &data[0])//扩容后修改值s[0] = 10fmt.Println(s, data)
}

  • slice中cap重新分配规律 

        从下面输出可以看出,slice扩容通常是以两倍原来容量重新分配底层数组。

        建议一次性分配足够大的空间,以减少内存分配和数据复制的开销,或者初始化足够长的len属性,改用索引号进行操作。

        及时释放不再使用的slice对象,避免持有过期数组,造成GC无法回收。

package mainimport "fmt"func main() {s := make([]int, 0, 2)c := cap(s)for i := 0; i < 50; i++ {s = append(s, i)if c < cap(s) {fmt.Printf("cap: %d->%d\n", c, cap(s))c = cap(s)}}
}

9. 切片拷贝

        函数copy在两个slice间复制数据,复制长度以len小的为准。两个slice可指向同一底层数组,允许元素区间重叠(本来创建切片不同的slice也可以指向同一个底层数组)。

        应及时将所需数据copy到较小的slice,以便释放超大号底层数组内存。

10. slice遍历

        slice遍历实际和数组遍历一样。

package mainimport "fmt"func main() {data := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}s1 := data[:]for index, value := range s1 {fmt.Printf("s1[%d] = %d\n", index, value)}fmt.Println("-----------------------------")for i := 0; i < len(s1); i++ {fmt.Printf("s1[%d] = %d\n", i, s1[i])}
}

11. 切片调整大小

        由于底层数组相同,且数组空间连续。所以可以像下面赋值。

12. 数组和切片的内存分布

        底层数组不一定要声明出来,比如:make的切片,或者 s := []int{1, 2, 3}

13.  字符串和切片 

 string底层就是一个byte的数组,因此,也可以进行切片操作。

package mainimport "fmt"func main() {str := "hello world"s := str[:5]fmt.Println(s)s1 := str[6:]fmt.Println(s1)
}

  • 修改字符串 

string本身是不可修改的,要修改string,需要先转成[]byte或[]rune,进行修改,再转成string。

英文字符串

package mainimport "fmt"func main() {str := "hello world"//字符串不能修改// s1 := str[:8]// s1[6] = 'G'// str[6] = 'G'//转成[]bytebt := []byte(str) //中文字符需要用[]rune(str)//修改[]bytebt[6] = 'G'bt = bt[:8]bt = append(bt, '!')str = string(bt)fmt.Println(str)
}

含中文字符串

package mainimport "fmt"func main() {str := "你好 世界 hello world"//字符串不能修改// s1 := str[:8]// s1[6] = 'G'// str[6] = 'G'rn := []rune(str)//修改[]bytern[3] = '够'rn[4] = '浪'rn = rn[:12]rn = append(rn, '好')//再转回字符串str = string(rn)fmt.Println(str)
}

  • 数组或切片转字符串 
strings.Replace(strings.Trim(fmt.Sprint(array_or_slice), "[]"), "", ",", -1)

 golang slice data[:6:8]两个冒号的理解:

        对于data := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

        常规slice,data[6:8],从data数组索引6到索引7,长度len为2,最大可扩充长度cap为4(slice底层数组为data,指针指向索引6位置,cap从索引6位置到data最后,即6-9,所以cap为4)。

        另一种写法:data[:6:8]每一个数字前面都有冒号,slice内容为data索引从0到6,长度len为6,最大扩充项cap设置为8。

        a[x:y:z]切片内容[x:y](前闭后闭),切片长度:y-x,切片容量:z-x

package mainimport "fmt"func main() {data := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}s1 := data[6:8]fmt.Println(len(s1), cap(s1))//s2 := data[6: :8] 中间必须要有数字s2 := data[:6:8]fmt.Println(len(s2), cap(s2))fmt.Println(s2)
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/758327.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MATLAB和Python数值和符号计算可视化物理学气体动能和粒子速度

要点 Python物理学差分数值和符号计算 热动力学计算&#xff1a;统计力学&#xff0c;分子动力学模型 Python寻找弹性物体的运动&#xff0c;LAMMPS 分子动力学模拟器模拟2D气体分子&#xff0c;Python原子模拟绘图&#xff0c;Python数值计算原子平衡性&#xff0c;Python绘制…

Elasticsearch实战:索引阻塞 —— 数据保护的终极武器

文章目录 1、索引阻塞的种类2、什么时候使用阻塞&#xff1f;场景1&#xff1a;进行系统维护场景。场景2&#xff1a;保护数据不被随意更改场景。场景3&#xff1a;优化资源使用的场景。场景4&#xff1a;遵守安全规则场景。 3、添加索引阻塞API4、解除设置 API5、小结6、参考 …

Transformer位置编码(Position Embedding)理解

本文主要介绍4种位置编码&#xff0c;分别是NLP发源的transformer、ViT、Sw-Transformer、MAE的Position Embedding 一、NLP transformer 使用的是1d的绝对位置编码&#xff0c;使用sincos将每个token编码为一个向量【硬编码】 Attention Is All You Need 在语言中&#xff0…

FDU 2018 | 1. 求众数

文章目录 1. 题目描述2. 我的尝试 1. 题目描述 AcWing 3685 求众数 给定一个长度为 n 的整数序列&#xff0c;请你求出该序列的众数。 众数就是一个序列中出现次数最多的数字。 如果不唯一&#xff0c;则输出小的那个值。 输入格式 第一行输入一个整数 n&#xff0c;表示有 …

RPG Maker MV 踩坑八 仿新仙剑战斗物品指令菜单

仿新仙剑战斗物品指令菜单 遇到的坑坑一坑二解决方法 遇到的坑 上次做的额外战斗指令菜单和物品战斗指令菜单&#xff0c;突然发现一个大问题&#xff0c;漏风了&#xff01;&#xff01;&#xff01; 其实就是将底部漏出来了&#xff0c;这样整个UI就不完整了&#xff0c;算是…

Wpf-自定义状态控件

后端代码 public class AxisStatus : Control{static AxisStatus(){DefaultStyleKeyProperty.OverrideMetadata(typeof(AxisStatus), new FrameworkPropertyMetadata(typeof(AxisStatus)));}public CornerRadius CornerRadius{get > (CornerRadius)GetValue(CornerRadiusPro…

微服务day04(上)-- RabbitMQ学习与入门

1.初识MQ 1.1.同步和异步通讯 微服务间通讯有同步和异步两种方式&#xff1a; 同步通讯&#xff1a;就像打电话&#xff0c;需要实时响应。 异步通讯&#xff1a;就像发邮件&#xff0c;不需要马上回复。 两种方式各有优劣&#xff0c;打电话可以立即得到响应&#xff0c;但…

深度学习 | 神经网络

一、神经网络原理 1、神经元模型 虽然叫个神经元&#xff0c;但骨子里还是线性模型。 2、神经网络结构 顾名思义就是由很多个神经元结点前后相连组成的一个网络。虽然长相上是个网络&#xff0c;但是本质上是多个线性模型的模块化组合。 在早期也被称为 多层感知机 Multi-Layer…

Visual Studio 2013 - 调试模式下根据内存地址查看内存

Visual Studio 2013 - 调试模式下根据内存地址查看内存 1. 查看内存References 1. 查看内存 调试 -> 窗口 -> 内存 -> 内存1-4 References [1] Yongqiang Cheng, https://yongqiang.blog.csdn.net/

【质押空投】公链Zkasino

据深潮TechFlow报道&#xff0c;游戏公链Zkasino HyperChain宣布上线质押挖矿活动&#xff0c;参与者可通过将ETH跨链到Zkasino链的方式来获取ZKAS代币。本次活动总共将分配25%的总代币供应量给参与者。质押挖矿时间将通过倒计时的方式来限制参与人数&#xff0c;以保护早期质押…

Datawhale 零基础入门数据挖掘-Task1 赛题理解

一、 赛题理解 Tip:此部分为零基础入门数据挖掘的 Task1 赛题理解 部分&#xff0c;为大家入门数据挖掘比赛提供一个基本的赛题入门讲解&#xff0c;欢迎后续大家多多交流。 赛题&#xff1a;零基础入门数据挖掘 - 二手车交易价格预测 地址&#xff1a;零基础入门数据挖掘 -…

【代码分享】四十七种测试函数(关注可免费获取)

智能优化算法测试函数简介 智能优化算法测试函数是为了在优化算法研究和开发中测试算法性能的规范问题集合。这些测试函数模拟了真实世界优化问题的不同方面,包括局部最小值、最大值、全局最优解,以及多种复杂性如高维度、非线性、不连续等。优化算法,如遗传算法、粒子群优…

蓝桥杯之动态规划冲刺

文章目录 动态规划01背包小练一下01背包网格图上的DP完全背包 最长公共字符串最长递增子序列 动态规划 动态规划&#xff1a;确定好状态方程&#xff0c;我们常常是确定前 当状态来到 i 时&#xff0c;前 i 个物体的状态是怎么样的&#xff0c;我们并不是从一个点去考虑&#x…

突破编程_C++_C++11新特性(tuple)

1 std::tuple 简介 1.1 std::tuple 概述 std::tuple 是一个固定大小的不同类型值的集合&#xff0c;可以看作 std::pair 的泛化&#xff0c;即 std::pair 是 std::tuple 的一个特例&#xff0c;其长度受限为 2。与 C# 中的 tuple 类似&#xff0c;但 std::tuple 的功能更为强…

数据库基本介绍及编译安装mysql

目录 数据库介绍 数据库类型 数据库管理系统&#xff08;DBMS&#xff09; 数据库系统 DBMS的工作模式 关系型数据库的优缺点 编译安装mysql 数据库介绍 数据&#xff1a;描述事物的的符号纪录称为数据&#xff08;Data&#xff09; 表&#xff1a;以行和列的形式组成…

mysql迁移达梦数据库 Java踩坑合集

达梦数据库踩坑合集 文章目录 安装达梦设置大小写不敏感Spring boot引入达梦驱动&#xff08;两种方式&#xff09;将jar包打入本地maven仓库使用国内maven仓库&#xff08;阿里云镜像&#xff09; 达梦驱动yml配置springboot mybatis-plus整合达梦,如何避免指定数据库名&…

CCAA审核员考试认证通用基础--合格评定基础练习题

合格评定基础练习题 一、单项选择题 1.与适用相同的规定要求、具体规则与程序的特定的合格评定对象有关的合格评定制度是&#xff08; &#xff09;。 A.合格评定 B 合格评定制度. C.合格评定方案 D.合格评定规范 2.认证也被称为&#xff08; &#xff09; A.证明 B.…

常用负载均衡详解

一、介绍 在互联网场景下&#xff0c;负载均衡&#xff08;Load Balance&#xff09;是分布式系统架构设计中必须考虑的一个环节&#xff0c;它通常是指将负载流量&#xff08;工作任务、访问请求&#xff09;平衡、分摊到多个操作单元&#xff08;服务器、组件&#xff09;上去…

Vue 计算属性和watch监听

1.1.计算属性 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><!-- 引入vue.js --><script src"node_modules/vue/dist/vue.js"></script> </h…

MySQL 快速学习路径

整理了下MySQL的快速学习路径 最近因为失业一直在家&#xff0c;闲来没事打算学习一下&#xff0c;打发时间。 大家可以订阅关注加转发 \(^o^)/~MySQL安装(Mac系统) MySQL的启停登陆与退出 MySQL的目录结构 MySQL语法分类 DDL&#xff08;1&#xff09; MySQL语法分类 DDL…