Go语言学习5-切片类型

2023-10-27

引言

上篇我们介绍了 Go语言的数组类型,本篇将介绍Go语言的切片类型。主要如下:

1. 切片

切片可以看作是对数组的一种包装形式。切片包装的数组称为该切片的底层数组。切片是针对其底层数组中某个连续片段的描述符。

1.1 类型表示法

对于一个元素类型为T的切片类型来说,它的类型字面量就是:

[]T

可以看出,长度并不是切片类型的一部分(即它不会出现在表示切片类型的类型字面量中)。另外,切片的长度是可变的。相同类型的切片值可能会有不同的长度。

切片类型声明中的元素类型也可以是任意一个有效的Go语言数据类型。例如:

[]rune

如上用于表示元素类型为rune的切片类型。

同样可以把一个匿名结构体类型作为切片类型的元素类型。例如:

[] struct {name, department string}

1.2 值表示法

和数组类似,也是复合字面量中的一种,例如:

[]string{"Go", "Python", "Java", "C", "C++", "PHP"}

在切片值所属的类型中根本就没有关于长度的规定。以下切片是合法的:

[]string{8: "Go", 2: "Swift", "Java", "C", "C++", "PHP"}

上面的等同于下面的复合字面量:

[]string{0: "", 1: "", 2: "Swift", 3: "Java", 4: "C", 5: "C++", 6: "PHP", 7: "", 8: "Go"}

1.3 属性和基本操作

切片类型的零值为nil。在初始化之前,一个切片类型的变量值为nil

切片类型中虽然没有关于长度的声明,但是值是有长度的,体现在它们所包含的元素值的实际数量。可以使用内建函数len来获取切片值的长度。例如:

len([]string{8: "Go", 2: "Swift", "Java", "C", "C++", "PHP"})

上面计算的结果值为9,这个切片值实际包含了6个被明确指定的string类型值和3个被填充的string类型的零值“”。

注意:在切片类型的零值(即nil)上应用内建函数len会得到0。

切片值的底层实现方式:
一个切片值总会持有一个对某个数组值的引用。一个切片值一旦被初始化,就会与一个包含了其中元素值的数组值相关联。这个数组值被称为引用他的切片值的底层数组。

多个切片值可能会共用一个底层数组。例如,如果把一个切片值复制成多个,或者针对其中的某个连续片段在切片成新的切片值,那么这些切片值所引用的都会是同一个底层数组。对切片值中的元素值的修改,实质上就是对其底层数组上的对应元素的修改。反过来讲,对作为底层元素的数组值中的元素值的改变,也会体现到引用该底层数组其包含该元素值的所有切片值上。

除了长度之外,切片值还有一个很重要的属性-----容量。切片值的容量与它所持有的底层数组的长度有关。可以通过内建函数cap来获取它。例如:

cap([]string{8: "Go", 2: "Swift", "Java", "C", "C++", "PHP"})

该切片值的容量是9,就等于它的长度。这是个特例,但很多情况下不是这样,且听慢慢道来。

切片值的底层数据结构:
一个切片值的底层数据结构包含了一个指向底层数组的指针类型值,一个代表了切片长度的int类型值和一个代表了切片容量的int类型值。
这里写图片描述

可以使用切片表达式从一个数组值或者切片值上”切”出一个连续片段,并生成一个新的切片值。例如:

array1 := [...]string{"Go", "Swift", "Java", "C", "C++", "PHP"}
slice1 := array1[:4]

变量slice1的值的底层数组实际上就是变量array1的值,如下图:

这里写图片描述

经过上面的描述,大家可能认为一个切片的容量可能就是其底层数组的长度。但事实并非如此,这里再创建一个切片值。例如:

slice2 := array1[3:]

变量slice2的值的底层数组也是变量array1的值,如下图:

这里写图片描述

如上所示slice2的值的容量与array1的值的长度并不相等。实际上,一个切片值的容量是从其中的指针指向的那个元素值到底层数组的最后一个元素值的计数值。切片值的容量的含义是其能够访问到的当前底层数组中的元素值的最大数量。

可以对切片值进行扩展,以查看更多底层数组元素。但是,并不能直接通过再切片的方式来扩展窗口。例如对于上面原始的slice1的值进行如下操作:

slice1[4]

这会引起一个运行时恐慌,因为其中的索引值超出了这个切片值当前的长度,这是不允许的。正确拓展的方式如下:

slice1 = slice1[:cap(slice1)]

通过再切片的方式把slice1扩展到了最大,可以看到最多的底层数组元素值了。这时slice1的值的长度等于其容量。

注意:一个切片值的容量是固定的。也就是说,能够看到的底层数组元素的最大数量是固定的。

不能把切片值扩展到其容量之外,例如:

slice1 = slice1[:cap(slice1)+1] // 超出slice1容量的范围,这样会引起一个运行时恐慌

一个切片值只能向索引递增的方向扩展。例如:

slice2 = slice2[-2:] // 这会引起一个运行时恐慌。另外,切片值不允许由负整数字面量代表。

使用append函数来扩展一开始的slice1的值:

slice1 = append(slice1, "Ruby", "Erlang")

执行该语句后,切片类型变量slice1的值及其底层数组(数组变量array1的值)的状态,如下图:

这里写图片描述

可以看出,slice1的值的长度已经由原来的4增长到了6,与它的容量是相同的。但是由于这个值的长度还没有超出它的容量,所以没必要再创建出一个新的底层数组出来。

原来的slice1的值为:

[]string{"Go", "Python", "Java", "C"}

现在的slice1的值为:

[]string{"Go", "Python", "Java", "C", "Ruby", "Erlang"}

原来的array1的值为:

[6]string{"Go", "Python", "Java", "C", "C++", "PHP"}

现在的array1的值为:

[6]string{"Go", "Python", "Java", "C", "Ruby", "Erlang"}

对现在的slice1再进行扩展,如下:

slice1 = append(slice1, "Lisp")

执行这条语句后,变量slice1的值的长度就超出了它的容量。这时将会有一个新的数组值被创建并初始化。这个新的数组值将作为在append函数新创建的切片值的底层数组,并包含原切片值中的全部元素值以及作为扩展内容的所有元素值。新切片值中的指针将指向其底层数组的第一个元素值,且它长度和容量都与其底层数组的长度相同。最后,这个新的切片值会被赋给变量slice1。

可以使用append函数把两个元素类型相同的切片值连接起来。例如:

slice1 = append(slice1, slice...)

当然也可以把数组值作为第二个参数传递给append函数。

即使切片类型的变量的值为零值nil,也会被看作是长度为0的切片值。例如:

slice2 = nil
slice2 = append(slice2, slice1...)

或者如下:

var slice4 []string
slice4 = append(slice4, slice...)

上面第一条语句用于声明(不包含初始化)一个变量。以关键字var作为开始,并后跟变量的名称和类型。未被初始化的切片变量的值为nil

1.4 切片使用的复杂用法

切片表达式中添加第三个索引-----容量上界索引。如果被指定,那么切片表达式的求值结果的那个切片值的容量就不再是该切片表达式的操作对象的容量与该表达式中的元素下界索引之间的差值,而是容量上界索引与元素下界索引之间的差值。

指定容量上界索引的目的就是为了减小新切片值的容量,可以允许更加灵活的数据隔离策略。

var array2 [10]int = [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
slice5 := array2[2:6]

如上我们可以直接访问和修改array2中对应索引值在[2,6)范围之内的元素值。

slice5 = slice5[:cap(slice5)]

如上再切片后,可以访问和修改array2的值中对应索引值在[2,10)范围之内的元素值。

如果slice5的值作为数据载体传递给了另一个程序,那么这个程序可以随意地更改array2的值中的某些元素值。这就等于暴露了程序中的部分实现细节,并公开了一个可以间接修改程序内部状态的方法,而往往这并不是我们想要的。

如果这样声明slice5:

slice5 := array2[2:6:8]

这样slice5的持有者只能访问和修改array2的值中对应索引值在[2,8)范围之内的元素值。

slice5 = slice5[:cap(slice5)]

即使将slice5扩展到最大,也不能通过它访问到array2的值中对应索引值大于等于8的那些元素。此时,slice5的值的容量为6(容量上界索引与元素下界索引的差值)。对于切片操作来说,被操作对象的容量是一个不可逾越的限制。slice5的值对其底层数组(array2的值)的“访问权限”得到了限制。

如果在slice5的值之上的扩展超出了它的容量:

slice5 = append(slice5, []int{10, 11, 12, 13, 14, 15})

那么它原有的底层数组就会被替换。也就彻底切断了通过slice5访问和修改其原有底层数组中的元素值的途径。

切片表达式中的3个索引的限制:当在切片表达式中指定容量上界索引的时候,元素上界索引是不能够省略。但是,在这种情况下元素下界索引却是可以省略的。例如:

slice5[:3:5]//合法的切片表达式
slice5[0::5]//非法的切片表达式,会造成一个编译错误

批量复制切片值中的元素

sliceA := []string{"Notepad", "UltraEdit", "Eclipse"}
sliceB := []string{"Vim", "Emacs", "LiteIDE", "IDEA"}

使用Go语言的内建函数copy,将变量sliceB的值中的元素复制到sliceA的值中。例如:

n1 := copy(sliceA,sliceB)

内建函数copy的作用是把源切片值(第二个参数值)中的元素值复制到目标切片值(第一个参数值)中,并且返回被复制的元素值的数量。copy函数的两个参数的元素类型必须一致,且它实际复制的元素值的数量将等于长度较短的那个切片值的长度。

变量n1的值为3, 变量sliceA的值被修改为:

[]string{"Vim", "Emacs", "LiteIDE"}

总结

本篇介绍了Go语言的 切片类型,下一篇介绍 Go语言的字典类型,敬请期待!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Go语言学习5-切片类型 的相关文章

  • Go语言学习16-特殊流程控制

    特殊流程控制 引言 特殊流程控制 1 defer语句 2 异常处理 2 1 error 2 2 panic 2 3 recover 结语 引言 上一篇博文介绍了 Go 语言的基本流程控制 本篇我们介绍 Go 语言的特殊流程控制 特殊流程控制
  • golang 单元测试、性能测试、性能监控技术

    golang 单元测试 性能测试 性能监控技术 go语言提供了强大的测试工具 下面举例简单介绍一下 go test 单元测试 go test bench 性能测试 go tool pprof 性能监控 go test 单元测试 例如对包he
  • Go语言学习19-样本测试

    样本测试 引言 样本测试 1 编写样本测试函数 2 样本测试的运行 3 样本测试函数的命名 结语 引言 上一篇笔者介绍了 Go 语言的 基准测试 其实在测试源码文件中还可以添加样本测试函数 但编写样本测试函数不需要使用 testing 代码
  • Go 语言运行时环境变量快速导览

    原文 http dave cheney net 2015 11 29 a whirlwind tour of gos runtime environment variables Go 语言运行时环境变量快速导览 介绍 Go Runtime除
  • go : GoLand安装及环境配置

    前因后果 新学期新气象 开学的第一节课是zw老师的区块链技术与应用领域 congratulations 涉及编程实验 使用Go语言 需要安装GoLand软件进行下一步 Go语言下载地址 golang下载链接 进去之后选择对应的版本下载 这里
  • Go语言学习15-基本流程控制

    基本流程控制 流程控制对比 Go 和 C 基本流程控制 1 代码块和作用域 2 if 语句 3 switch语句 3 1 表达式switch语句 3 2 类型switch语句 4 for 语句 4 1 for 子句 4 2 range 子句
  • Go语言学习13-类型转换

    类型转换 引言 类型转换 1 概念 2 数值类型之间的转换 3 与string类型相关的转换 4 别名类型值之间的转换 结语 引言 在上一篇博文中 我们介绍了 Go 语言的 数据的使用 本篇博文 我们将介绍 Go 语言的类型转换 类型转换
  • 【Go语言教程】(一) 下载、安装、配置

    1 下载 官网安装包下载地址为 https golang org dl 如果打不开可以使用这个地址 https golang google cn dl 找到适合你系统的版本下载 我下载的是windows版本 2 安装 msi文件点击完成安装
  • 学习笔记(02):go快速入门-iota用法

    立即学习 https edu csdn net course play 26897 344142 utm source blogtoedu
  • 【笔记】Go语言 Http-client 解析json后并插入数据库

    一 Http client 获取json 参考 Go标准库http Client的连接行为控制详解 原创手记 慕课网 因为 代码 resp err http Get http example com resp err http Post h
  • Go语言学习-基本

    命名 如果是在函数外部定义 那么将在当前包的所有文件中都可以访问 名字的开头字母的大小写决定了名字在包外的可见性 如果一个名字是大写字母开头的 译注 必须是在函数外部定义的包级名字 包级函数名本身也是包级名字 那么它将是导出的 也就是说可以
  • go语言RSA加密算法(一):生成密钥对

    生成密钥对 分别存储到公钥文件 files public pem 私钥文件中 files private pem package main import crypto rand crypto rsa crypto x509 encoding
  • go语言连接mysql数据库,并验证连通性

    go语言连接mysql数据库 并验证连通性 package main import database sql sql Open加载包 github com go sql driver mysql 没用到包里的内容但是需要加载一下这个包 lo
  • Go Web编程实战(3)----数据类型

    目录 前言 布尔型 数字类型 字符串类型 使用 byte修改 使用 rune修改 指针类型 指针的简单用法 修改指针值 复合类型 数组类型 结构体介绍 切片类型 从指定范围生成切片 重置切片 直接声明切片 Map 前言 Go语言数据类型包括
  • Go基础(包、变量和函数):开启Go语言之旅

    开启Go语言之旅 Go编程语言是一个开源项目 可以让程序员提高工作效率 Go是富有表现力 简洁 干净和高效的 其并发机制使编写充分利用多核和联网机器的程序变得容易 而其新颖类型系统则可实现灵活的模块化程序构建 快速编译为机器代码 但具有垃圾
  • Go语言学习18-基准测试

    基准测试 引言 基准测试 1 编写基准测试函数 2 计时器 3 内存分配统计 4 基准测试的运行 结语 引言 所谓基准测试 Benchmark Test 简称BMT 是指 通过一些科学的手段实现对一类测试对象的某项性能指标进行可测量 可重复
  • GO学习 --- 匿名函数

    一 匿名函数 Go支持匿名函数 如果我们某个函数只是希望使用一次 可以考虑使用匿名函数 匿名函数也可以实现多次调用 二 使用方式 方式一 在定义匿名函数时就直接调用 匿名函数 package main import fmt func mai
  • 使用go语言整合gin,驱动bartender打印标签程序

    可以用来当中间件 项目地址 github GitHub wjdsg0327 printer barTender 使用go语言整合gin驱动bartender打印标签 gitee printer barTender 使用go整合gin驱动ba
  • 【Go语言核心手册11】context.Context

    往期精选 欢迎转发 如何看待程序员35岁职业危机 Java全套学习资料 14W字 耗时半年整理 我肝了三个月 为你写出了GO核心手册 消息队列 从选型到原理 一文带你全部掌握 肝了一个月的ETCD 从Raft原理到实践 更多 11 1 内容
  • go语言学习 1 -- 类型

    Go语言接受了函数式编程的一些想法 支持匿名函数与闭包 接受了以Erlang语言为代表的面向消息编程思想 支持goroutine和通道 并推荐使用消息而不是共享内存来进行并发编程 总体来说 Go语言是一个非常现代化的语言 精小但非常强大 学

随机推荐