Golang语言基础之切片
概述
数组的长度是固定的并且数组长度属于类型的一部分,所以数组有很多的局限性func arraySum(x [3]int) int{sum := 0for _, v := range x{sum = sum + v}return sum
}
这个求和函数只能接受
[3]int
类型,其他的都不支持。
切片
切片(Slice)是一个拥有相同类型元素的可变长度的序列。它是基于数组类型做的一层封装。它非常灵活,支持自动扩容。切片是一个引用类型,它的内部结构包含地址
、长度
和容量
。切片一般用于快速地操作一块数据集合。
切片的定义
声明切片类型的基本语法如下:var name []T
其中,
- name:表示变量名
- T:表示切片中的元素类型
func main() {// 声明切片类型var a []string //声明一个字符串切片var b = []int{} //声明一个整型切片并初始化var c = []bool{false, true} //声明一个布尔切片并初始化var d = []bool{false, true} //声明一个布尔切片并初始化fmt.Println(a) //[]fmt.Println(b) //[]fmt.Println(c) //[false true]fmt.Println(a == nil) //truefmt.Println(b == nil) //falsefmt.Println(c == nil) //false// fmt.Println(c == d) //切片是引用类型,不支持直接比较,只能和nil比较
}
切片的长度和容量
切片拥有自己的长度和容量,我们可以通过使用内置的len()函数求长度,使用内置的cap()函数求切片的容量。基于数组定义切片
由于切片的底层就是一个数组,所以我们可以基于数组定义切片。func main() {// 基于数组定义切片a := [5]int{55, 56, 57, 58, 59}b := a[1:4] //基于数组a创建切片,包括元素a[1],a[2],a[3]fmt.Println(b) //[56 57 58]fmt.Printf("type of b:%T\n", b) //type of b:[]int
}
还支持如下方式:
c := a[1:] //[56 57 58 59]
d := a[:4] //[55 56 57]
e := a[:] //[55 56 57 58 59]
切片再切片
除了基于数组得到切片,我们还可以通过切片来得到切片。func processSlice2() {a := [...]string{"北京", "上海", "广州", "深圳", "成都", "重启"}fmt.Printf("a:%v type:%T len:%d cap:%d\n", a, a, len(a), cap(a))b := a[1:3]fmt.Printf("b:%v type:%T len:%d cap:%d\n", b, b, len(b), cap(b))c := b[2:5]fmt.Printf("c:%v type:%T len:%d cap:%d\n", c, c, len(c), cap(c))
}
输出:
a:[北京 上海 广州 深圳 成都 重启] type:[6]string len:6 cap:6
b:[上海 广州] type:[]string len:2 cap:5
c:[深圳 成都 重启] type:[]string len:3 cap:3
理解:从b再进行切片 实际也是从a切片 只不过b切片后b := a[1:3]
中的1不存在了 而在c := b[2:5]
中的5是存在的
注意:
对切片进行再切片时,索引不能超过原数组的长度,否则会出现索引越界的错误。
使用make()函数构造切片
我们上面都是基于数组来创建的切片,如果需要动态的创建一个切片,我们就需要使用内置的make()
函数,格式如下:
make([]T, size, cap)
其中:
- T:切片的元素类型
- size:切片中元素的数量
- cap:切片的容量
func main() {a := make([]int, 2, 10)fmt.Println(a) //[0 0]fmt.Println(len(a)) //2fmt.Println(cap(a)) //10
}
上面代码中
a
的内部存储空间已经分配了10个,但实际上只用了2个。
容量并不会影响当前元素的个数,所以
len(a)
返回2,
cap(a)
则返回该切片的容量。
切片的本质
切片的本质就是对底层数组的封装,它包含了三个信息:底层数组的指针、切片的长度(len)和切片的容量(cap)。举个例子,现在有一个数组a := [8]int{0, 1, 2, 3, 4, 5, 6, 7}
,切片s1 := a[:5]
,相应示意图如下。

切片s2 := a[3:6]
,相应示意图如下:

切片不能直接比较
切片之间是不能比较的,我们不能使用==
操作符来判断两个切片是否含有全部相等元素。
切片唯一合法的比较操作是和
nil
比较。
一个
nil
值的切片并没有底层数组,一个
nil
值的切片的长度和容量都是0。但是我们不能说一个长度和容量都是0的切片一定是
nil
,例如下面的示例:
var s1 []int //len(s1)=0;cap(s1)=0;s1==nil
s2 := []int{} //len(s2)=0;cap(s2)=0;s2!=nil
s3 := make([]int, 0) //len(s3)=0;cap(s3)=0;s3!=nil
所以要判断一个切片是否是空的,要是用
len(s) == 0
来判断,不应该使用
s == nil
来判断。
切片的赋值拷贝
下面的代码中演示了拷贝前后两个变量共享底层数组,对一个切片的修改会影响另一个切片的内容,这点需要特别注意。func main() {s1 := make([]int, 3) //[0 0 0]s2 := s1 //将s1直接赋值给s2,s1和s2共用一个底层数组s2[0] = 100fmt.Println(s1) //[100 0 0]fmt.Println(s2) //[100 0 0]
}
切片遍历
切片的遍历方式和数组是一致的,支持索引遍历和for range
遍历。
func processSliceRange() {s := []int{1, 2, 3, 4, 5}for i := 0; i < len(s); i++ {fmt.Println(s[i])}for index, value := range s {fmt.Printf("key:%d\tvalue:%d\n", index, value)}
}
append()方法为切片添加元素
Go语言的内建函数append()
可以为切片动态添加元素。
每个切片会指向一个底层数组,这个数组能容纳一定数量的元素。当底层数组不能容纳新增的元素时,切片就会自动按照一定的策略进行“扩容”,此时该切片指向的底层数组就会更换。“扩容”操作往往发生在
append()
函数调用时。
举个例子:
func processSliceAppend() {//append()添加元素和切片扩容var numSlice []intfor i := 0; i < 10; i++ {numSlice = append(numSlice, i)fmt.Printf("%v len:%d cap:%d ptr:%p\n", numSlice, len(numSlice), cap(numSlice), numSlice)}
}
输出:
[0] len:1 cap:1 ptr:0xc00001c0a8
[0 1] len:2 cap:2 ptr:0xc00001c0f0
[0 1 2] len:3 cap:4 ptr:0xc0000121c0
[0 1 2 3] len:4 cap:4 ptr:0xc0000121c0
[0 1 2 3 4] len:5 cap:8 ptr:0xc00001a2c0
[0 1 2 3 4 5] len:6 cap:8 ptr:0xc00001a2c0
[0 1 2 3 4 5 6] len:7 cap:8 ptr:0xc00001a2c0
[0 1 2 3 4 5 6 7] len:8 cap:8 ptr:0xc00001a2c0
[0 1 2 3 4 5 6 7 8] len:9 cap:16 ptr:0xc000014280
[0 1 2 3 4 5 6 7 8 9] len:10 cap:16 ptr:0xc000014280
从上面的结果可以看出:
append()
函数将元素追加到切片的最后并返回该切片。- 切片numSlice的容量按照1,2,4,8,16这样的规则自动进行扩容,每次扩容后都是扩容前的2倍。
例如:
func processSliceAppendAll() {var citySlice []string//追加一个元素citySlice = append(citySlice, "北京")//追加多个元素citySlice = append(citySlice, "广州", "深圳")//追加切片a := []string{"切片元素1,切片元素2"}citySlice = append(citySlice, a...)fmt.Println(citySlice)
}
删除切片里的元素
Go中没有现成可用的函数来删除切片里的元素,我们必须自己“发明”函数来删除切片元素。这里介绍一种方法:func main() {slice := []int{0, 1, 2, 3, 4, 5}fmt.Println(slice)//索引从0开始slice = RemoveIndex(slice, 3)fmt.Println(slice)
}
// RemoveIndex 删除索引
func RemoveIndex(s []int, index int) []int {
//s[:index] 包含我们想要删除的元素前面的所有元素(但不包含想要删除的元素其本身)
//s[index+1:] 包含我们想要删除的元素后面的所有元素(但不包含想要删除的元素其本身)
//… 将两个切片通过append()函数合并
return append(s[:index], s[index+1:]…)
}
输出:

切片元素排序
示例:func main() {ints := []int{2, 3, -1, 4, 7, 5}fmt.Println("排序前:", ints)//正序排sort.Ints(ints)fmt.Println("排序后(正序):", ints)sort.Sort(sort.Reverse(sort.IntSlice(ints)))fmt.Println("排序后(倒序):", ints)
floats := []float64{1.1, 2.3, 0.4, -9.5, 10} fmt.Println("排序前:", floats) sort.Float64s(floats) fmt.Println("排序后(正序):", floats) sort.Sort(sort.Reverse(sort.Float64Slice(floats))) fmt.Println("排序后(倒序):", floats)strings := []string{"aa", "a", "A", "Aa", "aab"} fmt.Println("\n排序前:", strings) sort.Strings(strings) fmt.Println("排序后(正序):", strings) sort.Sort(sort.Reverse(sort.StringSlice(strings))) fmt.Println("倒序后(倒序):", strings)
}
使用了Go语言的排序包sort
来对一个整数切片进行排序。
sort.Ints(ints)
sort.Ints
函数对整数切片
ints
进行升序排序。该函数修改原始切片,而不返回新的切片。所以
ints
切片现在是按照升序排列的。
fmt.Println("排序后(正序):", ints)
ints
,此时它是升序排列的。
sort.Sort(sort.Reverse(sort.IntSlice(ints)))
ints
进行了降序排序。首先,
sort.IntSlice(ints)
将整数切片转换为
sort.Interface
类型,以便在通用的
sort.Sort
函数中使用。然后,
sort.Reverse
对
sort.Interface
类型进行逆序操作,最后
sort.Sort
函数对整个切片进行排序。
fmt.Println("排序后(倒序):", ints)
ints
,此时它是降序排列的。
思考题
package main
import (
“fmt”
)
func main() {
var numbers4 = […]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
myslice := numbers4[4:6:8]
fmt.Printf(“myslice为 %d, 其长度为: %d\n”, myslice, len(myslice))
<span class="nx">myslice</span> <span class="p">=</span> <span class="nx">myslice</span><span class="p">[:</span><span class="nb">cap</span><span class="p">(</span><span class="nx">myslice</span><span class="p">)]</span>
<span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">"myslice的第四个元素为: %d"</span><span class="p">,</span> <span class="nx">myslice</span><span class="p">[</span><span class="mi">3</span><span class="p">])</span>
}
为什么 myslice 的长度为2,却能访问到第四个元素
输出:
myslice为 [5 6], 其长度为: 2 myslice的第四个元素为: 8
解释:
func main() {var number4 = [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}//4 是切片的起始索引(包含)//6 是切片的结束索引(不包含)//8 是切片的容量myslice := number4[4:6:8]fmt.Printf("type:%T,value:%v,len:%d,cap:%d\n", myslice, myslice, len(myslice), cap(myslice))//cap 的容量是4 所以对应的索引和值分别是://0=>5 1=>6 2=>7 3=>8myslice = myslice[:cap(myslice)]fmt.Println(myslice[1])
}
由于切片里面是指针、长度、容量
slice类型是数组的一个引用,数组arr的地址0x83029,那么slice的数据结构是[ ptr | len| cap],ptr就是指向数组arr的,就是一个指针,即存放了地址0x83029,len存放了slice的长度,cap存放了slice的容量,但是这个容量是不可能超过arr的len的,所以这样看来slice是一个引用,存放的数据是在arr中,修改slice的数据,对应修改了数组的内容,所以
len是2,cap是4,那么它就可以通过ptr指针访问到arr后面的第四个元素。
相关文章:

Golang语言基础之切片
概述 数组的长度是固定的并且数组长度属于类型的一部分,所以数组有很多的局限性 func arraySum(x [3]int) int{sum : 0for _, v : range x{sum sum v}return sum } 这个求和函数只能接受 [3]int 类型,其他的都不支持。 切片 切片(Slic…...
SpringCloud-服务消费者Fegin调用时无法获取异常信息
一、前言 假设有以下需求: 服务消费者A调用服务提供者B往MySQL新增一条人员信息服务提供者做了一个逻辑判断:若无该人员信息则新增,若已存在该人员信息,则返回给消费者异常状态码及异常信息:“请勿添加重复数据” 问…...

re:invent 2023 Amazon Q 初体验
授权声明:本篇文章授权活动官方亚马逊云科技文章转发、改写权,包括不限于在 Developer Centre,知乎,自媒体平台,第三方开发者媒体等亚马逊云科技官方渠道 前言 亚马逊云科技在2023 re:Invent全球大会上宣布推出 Amazon…...

认知觉醒(四)
认知觉醒(四) 第三节 耐心:得耐心者得天下 20世纪八九十年代,金庸的武侠小说风靡全国。如今,虽然几十年过去了,金庸先生也已与世长辞,但他留下的作品依然广受欢迎,被奉为经典。如此成就,自然…...

AI模型部署 | onnxruntime部署YOLOv8分割模型详细教程
本文首发于公众号【DeepDriving】,欢迎关注。 0. 引言 我之前写的文章《基于YOLOv8分割模型实现垃圾识别》介绍了如何使用YOLOv8分割模型来实现垃圾识别,主要是介绍如何用自定义的数据集来训练YOLOv8分割模型。那么训练好的模型该如何部署呢?…...

模拟电路学习笔记(一)之芯片篇(持续更新)
模拟电路学习笔记(一)之芯片篇(持续更新) 1.CD4047BE芯片 CD4047是一种包含高电压的多谐振荡器,该器件的操作可以在两种模式下完成,分别是单稳态和非稳态。CD4047需要一个外部电阻器和电容器来决定单稳态…...

如何利用CentOS7+docker+jenkins+gitee部署springboot+vue前后端项目(保姆教程)
博主介绍:Java领域优质创作者,博客之星城市赛道TOP20、专注于前端流行技术框架、Java后端技术领域、项目实战运维以及GIS地理信息领域。 🍅文末获取源码下载地址🍅 👇🏻 精彩专栏推荐订阅👇🏻…...

qt 5.15.2 主窗体事件及绘制功能
qt 5.15.2 主窗体事件及绘制功能 显示主窗体效果图如下所示: main.cpp #include "mainwindow.h"#include <QApplication>int main(int argc, char *argv[]) {QApplication a(argc, argv);MainWindow w;w.setFixedWidth(600);w.setFixedHeight(6…...

(2)(2.4) TerraRanger Tower/Tower EVO(360度)
文章目录 前言 1 安装传感器并连接 2 通过地面站进行配置 3 参数说明 前言 TeraRanger Tower 可用于在 Loiter 和 AltHold 模式下进行目标规避。传感器的最大可用距离约为 4.5m。 TeraRanger Tower EVO 可用于在 Loiter 和 AltHold 模式下进行目标规避。传感器的最大可用…...

Redis_主从复制、哨兵模式、集群模式详解
Redis的主从复制 为什么Redis要引入主从复制?what? 在这里博主为小伙伴们简单的做下解释,可以了解一下 实际生产环境下,单机的redis服务器是无法满足实际的生产需求的。 第一,单机的redis服务器很容易发生单点故障&am…...

关于神舟-战神TA5NS系统重装问题
加装固态卡在log处无法开机问题 下面是我的步骤 1.按f7选择pe安装系统,然后发现卡在战神log处不转动 2.下载驱动 TA5NS驱动地址 下载RAID驱动(如果没有私信我,我网盘里有),拷到u盘中,然后进入pe系统里面…...

前端大文件上传webuploader(react + umi)
使用WebUploader还可以批量上传文件、支持缩略图等等众多参数选项可设置,以及多个事件方法可调用,你可以随心所欲的定制你要的上传组件。 分片上传 1.什么是分片上传 分片上传,就是将所要上传的文件,按照一定的大小,将…...
人大金仓(kingbase)数据库常用sql命令
一. 字段 1. 添加 alter table book add column book_id varchar not null, book_title varchar(10) default ;2. 删除 alter table book drop book_id, book_title;// 外键时 alter table book drop book_id, book_title cascade;3. 修改类型 alter table book alter colu…...

HashMap相关专题
前置知识:异或运算 异或运算介绍 异或有什么神奇之处(应用)? (1)快速比较两个值 (2)我们可以使用异或来使某些特定的位翻转,因为不管是0或者是1与1做异或将得到原值的相…...
threejs WebGLRenderer 像素比对画布大小的影响
官方文档 - WebGLRenderer .setPixelRatio ( value : number ) : undefined 设置设备像素比。通常用于避免HiDPI设备上绘图模糊 .setSize ( width : Integer, height : Integer, updateStyle : Boolean ) : undefined 将输出canvas的大小调整为(width, height)并考虑设备像素比…...
RocketMQTemplate.send() 与 RocketMQTemplate.syncSend() 方法详解
Apache RocketMQ 是一款强大的分布式消息中间件,与 Spring Boot 集成后,通过 RocketMQTemplate 提供了多种方法来发送消息。其中,send() 和 syncSend() 是两个常用的发送消息方法,本文将深入探讨它们的区别以及详细解释这两个方法…...

波奇学C++:类型转换和IO流
隐式类型转换 int i0; double pi; 强制类型转换 int* pnullptr; int a(int)p; 单参数构造函数支持隐式类型转换 class A { public:A(string a):_a(a){} private:string _a; }; A a("xxxx"); //"xxx" const char* 隐式转换为string 多参数也可以通过{…...

集成开发环境 PyCharm 的安装【侯小啾python基础领航计划 系列(二)】
集成开发环境PyCharm的安装【侯小啾python基础领航计划 系列(二)】 大家好,我是博主侯小啾, 🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔…...

Java核心知识点整理大全27-笔记(已完结)
目录 30. 云计算 30.1.1. SaaS 30.1.2. PaaS 30.1.3. IaaS 30.1.4. Docker 30.1.4.1. 概念 30.1.4.2. Namespaces 30.1.4.3. 进程(CLONE_NEWPID 实现的进程隔离) 30.1.4.4. Libnetwork 与网络隔离 30.1.4.5. 资源隔离与 CGroups 30.1.4.6. 镜像与 UnionFS 30.1.4.7.…...
1. 使用poll或epoll创建echo服务器
1. 说明: 此篇博客主要记录一种客户端实现方式,和两种使用poll或者epoll分别创建echo服务器的方式,具体可看代码注释: 2. 相关代码: 2.1 echoClient.cpp #include <iostream> #include <cstdio> #incl…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)
HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...
AtCoder 第409场初级竞赛 A~E题解
A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...
.Net Framework 4/C# 关键字(非常用,持续更新...)
一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)
漏洞概览 漏洞名称:Apache Flink REST API 任意文件读取漏洞CVE编号:CVE-2020-17519CVSS评分:7.5影响版本:Apache Flink 1.11.0、1.11.1、1.11.2修复版本:≥ 1.11.3 或 ≥ 1.12.0漏洞类型:路径遍历&#x…...

FFmpeg:Windows系统小白安装及其使用
一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】,注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录(即exe所在文件夹)加入系统变量…...

stm32wle5 lpuart DMA数据不接收
配置波特率9600时,需要使用外部低速晶振...

Win系统权限提升篇UAC绕过DLL劫持未引号路径可控服务全检项目
应用场景: 1、常规某个机器被钓鱼后门攻击后,我们需要做更高权限操作或权限维持等。 2、内网域中某个机器被钓鱼后门攻击后,我们需要对后续内网域做安全测试。 #Win10&11-BypassUAC自动提权-MSF&UACME 为了远程执行目标的exe或者b…...