go 切片slice学习总结

2024-08-30 21:36
文章标签 go 切片 学习 总结 slice

本文主要是介绍go 切片slice学习总结,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

切片的结构

切片的底层结构:

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] += 100s[1] += 200fmt.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 := s2fmt.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的学习总结,若是不当之处,烦请指出。后面有新的学习领悟,会继续添加。 

这篇关于go 切片slice学习总结的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1121992

相关文章

go中的时间处理过程

《go中的时间处理过程》:本文主要介绍go中的时间处理过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1 获取当前时间2 获取当前时间戳3 获取当前时间的字符串格式4 相互转化4.1 时间戳转时间字符串 (int64 > string)4.2 时间字符串转时间

Go语言中make和new的区别及说明

《Go语言中make和new的区别及说明》:本文主要介绍Go语言中make和new的区别及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1 概述2 new 函数2.1 功能2.2 语法2.3 初始化案例3 make 函数3.1 功能3.2 语法3.3 初始化

JavaSE正则表达式用法总结大全

《JavaSE正则表达式用法总结大全》正则表达式就是由一些特定的字符组成,代表的是一个规则,:本文主要介绍JavaSE正则表达式用法的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录常用的正则表达式匹配符正则表China编程达式常用的类Pattern类Matcher类PatternSynta

Go语言中nil判断的注意事项(最新推荐)

《Go语言中nil判断的注意事项(最新推荐)》本文给大家介绍Go语言中nil判断的注意事项,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录1.接口变量的特殊行为2.nil的合法类型3.nil值的实用行为4.自定义类型与nil5.反射判断nil6.函数返回的

Go语言数据库编程GORM 的基本使用详解

《Go语言数据库编程GORM的基本使用详解》GORM是Go语言流行的ORM框架,封装database/sql,支持自动迁移、关联、事务等,提供CRUD、条件查询、钩子函数、日志等功能,简化数据库操作... 目录一、安装与初始化1. 安装 GORM 及数据库驱动2. 建立数据库连接二、定义模型结构体三、自动迁

Go语言代码格式化的技巧分享

《Go语言代码格式化的技巧分享》在Go语言的开发过程中,代码格式化是一个看似细微却至关重要的环节,良好的代码格式化不仅能提升代码的可读性,还能促进团队协作,减少因代码风格差异引发的问题,Go在代码格式... 目录一、Go 语言代码格式化的重要性二、Go 语言代码格式化工具:gofmt 与 go fmt(一)

SQL中JOIN操作的条件使用总结与实践

《SQL中JOIN操作的条件使用总结与实践》在SQL查询中,JOIN操作是多表关联的核心工具,本文将从原理,场景和最佳实践三个方面总结JOIN条件的使用规则,希望可以帮助开发者精准控制查询逻辑... 目录一、ON与WHERE的本质区别二、场景化条件使用规则三、最佳实践建议1.优先使用ON条件2.WHERE用

Go学习记录之runtime包深入解析

《Go学习记录之runtime包深入解析》Go语言runtime包管理运行时环境,涵盖goroutine调度、内存分配、垃圾回收、类型信息等核心功能,:本文主要介绍Go学习记录之runtime包的... 目录前言:一、runtime包内容学习1、作用:① Goroutine和并发控制:② 垃圾回收:③ 栈和

Go语言中泄漏缓冲区的问题解决

《Go语言中泄漏缓冲区的问题解决》缓冲区是一种常见的数据结构,常被用于在不同的并发单元之间传递数据,然而,若缓冲区使用不当,就可能引发泄漏缓冲区问题,本文就来介绍一下问题的解决,感兴趣的可以了解一下... 目录引言泄漏缓冲区的基本概念代码示例:泄漏缓冲区的产生项目场景:Web 服务器中的请求缓冲场景描述代码

Go语言如何判断两张图片的相似度

《Go语言如何判断两张图片的相似度》这篇文章主要为大家详细介绍了Go语言如何中实现判断两张图片的相似度的两种方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 在介绍技术细节前,我们先来看看图片对比在哪些场景下可以用得到:图片去重:自动删除重复图片,为存储空间"瘦身"。想象你是一个