go 切片slice学习总结

news2024/9/23 19:25:53

切片的结构

切片的底层结构:

type SliceHeader struct {
	Data uintptr  // 指向底层数组的指针 
	Len  int      //长度
	Cap  int      //空间容量
}

切片的初始化

1 通过数组或者已有的slice创建新的slice

1.1 使用数组创建切片

通过数组的一部分来初始化切片。

array := [10]int{0,1, 2, 3, 4, 5,6,7,8,9}  
slice := a[5:7] 

Slice将与原数组共用一部分内存。

1.2 通过slice创建新的切片对象

x := []int{2, 3, 5, 7, 11}  
y := x[1:3]

x:   长度len=5 cap=5 data指针指向长度为5的底层数组结构。

y  长度为2 cap为y底层原始数组结构第一个元素位置到的最后一个容量空间位置的长度即5-1=4

data为指针指向底层原始数组结构第1个元素的地址(索引从0开始)

图示如下:

2,使用make函数

make函数是Go中用于分配和初始化内置类型的内置函数,也可以用来创建切片。

slice := make([]int, 5, 10) // 创建一个长度为5、容量为10的int类型切片

长度为5,即可以使用下标slice[0] ~ slice[4]来操作里面的元素,capacity为10,表示后续向slice添加新的元素时可以不必重新分配内存,直接使用预留内存即可。

3,切片字面量

直接使用切片字面量创建切片。

s := []int{1, 2, 3} // 直接初始化一个切片


4,较特殊的切片:

nil 切片

var a []int 

未初始化的切片默认值为nil

空切片

var b = []int{} 

也叫零值切片。和 nil 不相等, 一般用来表示一个空的集合。len 和 cap 都为 0。空切片在内部拥有一个非nil的、零长度的底层数组


在判断一个切片是否为空时,一般通过 len 获取切片的长度来判断,一般很少将切片和 nil 值做直接的比较

数组与切片

数组截取元素生成切片,共用底层元素存储底层数组结构

func main() {
	data := [...]int{0, 1, 2, 3, 4, 5}

	s := data[2:4]
	s[0] += 100
	s[1] += 200

	fmt.Println(s)
	fmt.Println(data)
}

打印结果:

 [102 203]
 [0 1 102 203 4 5]

 切片赋值

	data := [...]int{0, 1, 2, 3, 4, 5}

	s2 := data[:]
	fmt.Println("s2是:", s2)

	s3 := s2
	fmt.Println("s3是:", s3)
	s3[0] = 1000 + s3[0]
	fmt.Println("s3是:", s3)
	fmt.Println("s2是:", s2)
    fmt.Println("data是:", data)

打印结果:

s3是: [1000 1 2 3 4 5]
s2是: [1000 1 2 3 4 5]
data是: [1000 1 2 3 4 5]

切片参数

函数参数是切片时,是引用传递,函数内对切片的改动影响源切片

    func main(){
    sl := []int{1, 2, 3, 4, 5, 6, 7}
	PrintElements(sl)
	fmt.Printf("slice a : %v\n", sl)
  }


func PrintElements(sl []int) {
	fmt.Println("-------------")
	for i, v := range sl {
		fmt.Println(v)
		sl[i] = v + 1
	}
}

打印结果:

-------------

 1
2
3
4
5
6
7
slice a : [2 3 4 5 6 7 8]

切片容量

切片扩容

扩容实际上是重新分配一块更大的内存,将原Slice数据拷贝进新Slice,然后返回新Slice,扩容后再将数据追加进去。

例如,当向一个capacity为5,且length也为5的Slice再次追加1个元素时,就会发生扩容,如下图所示:

扩容操作只关心容量,会把原Slice数据拷贝到新Slice,追加数据由append在扩容结束后完成。上图可见,扩容后新的Slice长度仍然是5,但容量由5提升到了10,原Slice的数据也都拷贝到了新Slice指向的数组中。

扩容容量的选择遵循以下规则:

  • 如果原Slice容量小于1024,则新Slice容量将扩大为原来的2倍;
  • 如果原Slice容量大于等于1024,则新Slice容量将扩大为原来的1.25倍;

使用append()向Slice添加一个元素的实现步骤如下:

  • 假如Slice容量够用,则将新元素追加进去,Slice.len++,返回原Slice
  • 原Slice容量不够,则将Slice先扩容,扩容后得到新Slice
  • 将新元素追加进新Slice,Slice.len++,返回新的Slice。

参考:slice-地鼠文档 

切片字面量创建的切片

    slice := []int{1, 2, 3, 4, 5, 6, 7, 8}

当你使用字面量来创建切片时,切片的初始容量(capacity)通常等于其长度(length)。这意味着,例子slice := []int{1, 2, 3, 4, 5, 6, 7, 8},切片的长度和容量都是8。

	slice := []int{1, 2, 3, 4, 5, 6, 7, 8}
	fmt.Print("cap:", cap(slice))
	fmt.Print("len:", len(slice))

cap: 8
len: 8 

扩容:

	slice := []int{1, 2, 3, 4, 5, 6, 7, 8}
	fmt.Println("cap:", cap(slice))
	fmt.Println("len:", len(slice))
	fmt.Println("----------append后扩容----------")
	slice2 := append(slice, 0) // 切片扩展 1 个空间
	fmt.Println("cap:", cap(slice2))
	fmt.Println("len:", len(slice2))
	fmt.Println(&slice[0] == &slice2[0])

 打印结构

cap: 8
len: 8
----------append后扩容----------
cap: 16
len: 9
false

append元素后容量不够使用,扩容为原来的两倍

make创建的切片

make创建的切片,类型申明之后,第一个数值是长度,第二个数值是容量

slice2 := make([]int, 3, 7)
	fmt.Println("cap:", cap(slice2))
	fmt.Println("len:", len(slice2))

cap: 7
len: 3

append元素后,容量不够就会扩容为之前的两倍容量。随之是地址的改动。

	slice2 := make([]int, 3, 7)
	fmt.Println("cap:", cap(slice2))
	fmt.Println("len:", len(slice2))
	slice3 := append(slice2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) // 切片扩展 1 个空间
	fmt.Println("----------append后扩容----------")
	fmt.Println("cap:", cap(slice3))
	fmt.Println("len:", len(slice3))
	fmt.Println(&slice2[0] == &slice3[0])

cap: 7
len: 3
----------append后扩容----------
cap: 14
len: 13
false

切片操作

1,append操作

使用append函数,生成新的切片

append函数返回的slice跟原slice是独立的slice,互不影响

    var a = []int{1, 2, 3}
	fmt.Printf("slice a : %v\n", a)
	var b = []int{4, 5, 6}
	fmt.Printf("slice b : %v\n", b)
	c := append(a, b...)
	fmt.Printf("slice c  : %v\n", c)
	b = append(b, 7)
	fmt.Printf("slice b  after apend 7: %v\n", b)
	a = append(a, 7)
	fmt.Printf("slice a  after apend 7: %v\n", a)
	fmt.Printf("slice c : %v\n", c)
	c = append(c, 7)
	fmt.Printf("slice c  after apend 7: %v\n", c)

打印结果:

slice a : [1 2 3]
slice b : [4 5 6]
slice c  : [1 2 3 4 5 6]
slice b  after apend 7: [4 5 6 7]
slice a  after apend 7: [1 2 3 7]
slice c : [1 2 3 4 5 6]
slice c  after apend 7: [1 2 3 4 5 6 7]

其中b...是使用...展开运算符将slice展开作为单独元素传递给函数使用

以上apend操作是在末尾添加,还可以在头部添加

s := []int{2, 3, 4, 5, 6, 7}
	s = append([]int{1}, s...)
	fmt.Println("s是:", s)
	s = append([]int{-1, -2}, s...)
	fmt.Println("s是:", s)

s是: [1 2 3 4 5 6 7]
s是: [-1 -2 1 2 3 4 5 6 7] 

2,切片copy

copy(dst,src)将src内容复制给dst切片

src := []int{1, 2, 3}
	dst := make([]int, 3)
	copy(dst, src) // 现在dst是[1, 2, 3]的拷贝
	fmt.Println("dst是:", dst)

copy复制后两个切片是独立的

src := []int{1, 2, 3}
	dst := make([]int, 3)
	copy(dst, src) // 现在dst是[1, 2, 3]的拷贝
	fmt.Println("dst是:", dst)
	dst = append(dst, 4)
	fmt.Println("dst是:", dst)
	fmt.Println("src是:", src)

 dst是: [1 2 3]
dst是: [1 2 3 4]
src是: [1 2 3]

copy与append实现高效 添加元素

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

具体实现:

a := []int{1, 2, 3, 4, 5, 6, 7, 8}
	a = append(a, 0)     // 切片扩展 1 个空间
	copy(a[4+1:], a[4:]) // a[i:] 向后移动 1 个位置
	a[4] = 88            // 设置新添加的元素
	fmt.Println("a是:", a)

打印:

a是: [1 2 3 4 88 5 6 7 8] 

3,切片删除 

从末尾删除

slice5 := []int{1, 2, 3, 4, 5, 6, 7, 8}
	new_slice5 := slice5[:len(slice5)-1]
	fmt.Println("new_slice5:", new_slice5)
	new_slice5 = slice5[:len(slice5)-5]
	fmt.Println("new_slice5:", new_slice5)

new_slice5: [1 2 3 4 5 6 7]
new_slice5: [1 2 3]

从头部删除 

    slice5 = slice5[1:]  //删除头部第1个元素

    slice5 = slice[5:]   //删除头部第N个元素

使用append删除

append删除元素非常灵活,可以删除头部,中间,尾部元素。注意若是希望地址不变,需要将append的结果赋值回去

 删除头部元素

slice5 = append(slice5[:0], slice5[3:]...) //删除头部元素
	fmt.Println("slice5:", slice5)

slice5: [4 5 6 7 8]

删除中间元素

	slice5 = append(slice5[:3], slice5[5:]...) //删除中间两个元素
	fmt.Println("slice5:", slice5)

slice5: [1 2 3 6 7 8]

删除尾部元素

slice5 = append(slice5[:0], slice5[0:len(slice5)-3]...) //删除尾部元素
	fmt.Println("slice5:", slice5)

slice5: [1 2 3 4 5] 

以上便是对slice的学习总结,若是不当之处,烦请指出。后面有新的学习领悟,会继续添加。 

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

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

相关文章

fixed、absolute 和 relative 布局

https://andi.cn/page/621716.html

计算机视觉编程 1(图片处理)

目录 灰色度 缩略图 拷贝粘贴区域 调整图像尺寸 旋转图像45 画图线、描点 灰色度 灰度是指图像中每个像素的亮度值,用来描述图像中各个像素的明暗程度。在计算机视觉中,灰度可以通过以下方式来计算: 1. 平均值法:将图像中每…

核心技术揭秘:AI、低代码与定制开发的三重奏,如何应对复杂业务需求

背景介绍 在当今迅速发展的数字化时代,企业面临的业务环境变得日益复杂和多变。无论是面对不断变化的市场需求,还是处理海量数据并从中获取洞察力,企业都需要快速响应并灵活调整自身的策略。传统的开发模式在应对这种复杂性时往往显得力不从心…

大模型提示词工程技术2-设计有效的提示词技巧、角色与上下文在提示中的应用

大模型提示词工程技术2-设计有效的提示词技巧、角色与上下文在提示中的应用。《大模型提示词工程技术》的作者:微学AI,这是一本专注于提升人工智能大模型性能的著作,它深入浅出地讲解了如何通过优化输入提示词来引导大模型生成高质量、准确的…

技术赋能数字化转型:百数低代码平台在试点城市的应用与实践

在当今信息化高速发展的时代,数字化转型已成为城市和企业发展的必然趋势。为了推动这一进程,数字化转型城市试点政策应运而生。“据了解,中央财政将对每个试点城市给予1亿元资金奖补,支持中小企业实施数字化改造,以数字…

5 - ZYNQ GPIO

文章目录 0 前言1 GPIO基本概念1.1 MIO-EMIO基本介绍1.2 MIO-EMIO连接情况 0 前言 本文来参考博主徐晓康的博文 ZYNQ7000-GPIO详解,仅作为个人学习记录。 1 GPIO基本概念 在ZYNQ中,GPIO(General Purpose Input/Output,通用输入…

嵌入式RTOS正在缩小与Linux系统之间的差异

RTOS与Linux的物联网设备操作系统之争已经持续了很多年。Linux以其强大的计算能力和丰富的软件生态,在需要复杂处理和软件支持的物联网设备上占据一席之地;RTOS凭借实时响应和资源节约的特性,在对实时性和资源占用有严格要求的场景中独领风骚。 如果时间倒回五年前,那…

[C++] C++11详解 (三)类的成员函数、完美转发

标题:[C] C11详解 (三)完美转发与lambda表达式 水墨不写bug 目录 一、C11新增两个类的默认成员函数 1.强制生成默认函数的关键字default: 2.禁止生成默认函数的关键字delete: 二、完美转发 正文开始: 一、C11新增两个类的默…

【YOLO5 项目实战】(8)PyQt5 图形界面—PCB缺陷检测系统

欢迎关注『youcans动手学模型』系列 本专栏内容和资源同步到 GitHub/youcans 【YOLO5 项目实战】(8)PyQt5 图形界面—PCB缺陷检测系统 1. PyQt5 图形界面开发工具1.1 PyQt5 的安装1.2 在 PyCharm 集成 QtDesigner 和 PyUIC1.3 使用 QtDesigner 开发 PyQt…

EPLAN在安装完成之后,输入文字时出现卡顿和死机的解决办法

EPLAN在安装完成之后,输入文字时出现卡顿和死机的解决办法 EPLAN在安装完成之后,插入文字时有时会卡顿,甚至出现软件卡死,无任何反映的情况,具体的解决办法可参考以下内容: 找到电脑右下角的输入法,右击进入设置, 如下图所示,点击进入常规设置, 如下图所示,向下找…

终于!我找到了开发的得力助手!阿里云天池云原生编程挑战赛参赛攻略

作者:ysevenk_7 参赛准备 我是机缘巧合在 6 月底了解到了天池云原生编程挑战赛,于是乎搜了一下,之前本人对于比赛并没有太多经验,看了大赛介绍之后莫名兴奋,果断拉了队友报名,完成认证、起队名、下载插件…

【STM32】RS485

RS485是常见的串口接口。 大部分图片来源:正点原子HAL库课程 专栏目录:记录自己的嵌入式学习之路-CSDN博客 目录 1 串口、UART、TTL、RS232、RS422、RS485的关系 1.1 串口 1.2 UART、TTL、RS232、RS422、RS485 1.3 常见串口标准的比较 …

Apache Arrow 的列式内存格式

Apache Arrow 的列式存储格式是一种内存数据组织标准,它通过物理布局、Array(数组)、Schema(模式)和 RecordBatch(记录批次)等,优化了大数据的存储与处理。这种格式以列而非行来存储…

更改网络ip地址时出现错误怎么办

在日常的网络使用中,‌有时我们需要更改IP地址以满足特定的网络需求,‌然而,‌在更改IP地址的过程中,‌可能会遇到各种错误,‌导致无法成功更改或网络连接出现问题。‌‌而更改网络IP地址时出现错误是由于多种原因导致…

二、基于Vue3的开发-环境搭建【Visual Studio Code】扩展组件

Visual Studio Code中的扩展组件 1、安装的扩展工具2、说明2.1 、代码规范性检查EsLint2.2 、代码语法高亮提示工具Vue - Official2.3 、阿里的AI代码开发提示工具 TONGYI Lingma 1、安装的扩展工具 2、说明 2.1 、代码规范性检查EsLint Visual Studio Code 中【设置】-setti…

基于元神操作系统编程写硬盘扇区

1. 背景 本文介绍了“调用元神操作系统API向硬盘扇区写数据”的程序实现及测试结果。 2. 方法 (1)调用元神操作系统API读硬盘扇区 本部分内容已在前面的文章中进行介绍,详细内容请参考“编写程序调用元神操作系统的API”。 (…

二叉树 - 二叉树的所有路径

257. 二叉树的所有路径 方法一:递归法: /*** Definition for a binary tree node.* function TreeNode(val, left, right) {* this.val (valundefined ? 0 : val)* this.left (leftundefined ? null : left)* this.right (rightundefi…

水下目标检测(低光照目标检测)方法-发表在Patter Recognition,代码已开源

这里写自定义目录标题 前言动机贡献Overview一些实验结果数据集主要实验结果实验结果展示 总结 前言 Hi,各位读者,好久不见!现在我已经从北大博士毕业,成为一名小青椒啦!工作还是需要宣传的。今天想分享我在水下目标检测的工作&a…

低代码技术:快速构建应用的未来

在当今快速发展的数字化时代,企业和个人对软件应用的需求不断增长。然而,传统的软件开发过程通常复杂且耗时。这使得低代码技术(Low-Code Technology)成为了越来越多人关注的焦点。本文将探讨低代码技术的基本概念、优势以及如何在…

贾湖刻符——汉字起源的重要线索

关注我们 - 数字罗塞塔计划 - 汉字是世界上唯一沿用至今的古老文字系统,其演变历程承载着中华文明的发展和赓续。那么汉字究竟源自何时?是古代神话传说的“昔者仓颉作书,而天雨粟,鬼夜哭”;还是由华夏先民创制的刻划符…