首页 > 其他分享 >Go-08 Golang中的切片

Go-08 Golang中的切片

时间:2022-12-04 23:22:56浏览次数:45  
标签:切片 nil 08 cap len Golang Println Go fmt

package main

import "fmt"

// Go 语言中的切片
/*	学习目标
	1.为什么要使用切片
	2.切片的定义
	3.关于nil的认识
	4.切片的循环遍历
	5.基于数组定义切片
	6.切片再切片
	7.关语切片的长度和容量
	8.切片的本质
	9.使用make()函数构造切片
	10.切片不能直接比较
	11.切片是引用数据类型--注意切片的赋值拷贝
	12.append()方法为切片添加元素
	13.切片的扩容策略
	14.使用copy()函数复制切片
	15.从切片中删除元素
	16.练习题
*/

func arraySum(x [4]int) int {
	sum := 0
	for _, v := range x {
		sum += v
	}
	return sum
}

func main() {
	// 1.为什么要使用切片
	// 因为数组的长度是固定的并且数组长度属于类型的一部分,所以数组有很多的局限性。例如:
	a_ := [4]int{1, 2, 3, 4}
	println("求和结果是:", arraySum(a_)) // 这个参数符合要求
	// b := [5]int{1,2,3,4,5}
	// println(arraySum(b))	// 这个直接报错,类型虽然正确,但是长度不匹配。b的长度是5,arraySum()接收的参数长度是4。
	// arraySum()这个函数只能接收[4]int类型,其他的都不支持。所以传入长度为5的数组的时候就会报错。
	/*	2.切片的定义
		切片是一个拥有相同类型元素的可变长度的序列。它是基于数组类型做的一层封装。它非常灵活,支持自动扩容。
		切片是一个 引用类型, 它的内部结构包括地址、长度和容量。
		声明切片类型的基本语法: var name []T	其中name是变量名称,T是切片的元素类型。
		2.1 声明切片类型,切片示例如下:
	*/
	var a []string              // 声明一个字符串类型的切片
	var b = []int{}             // 声明一个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) //true
	fmt.Println(b == nil) //false
	fmt.Println(c == nil) //false
	//fmt.Println(c == d)	// invalid operation: c == d (slice can only be compared to nil)
	/*	2.2 关于nil的认识
		切片记住一句话,切片只能和nil进行比较。切片未赋值时默认值是nil(例如上面a的默认值就是nil),
		一旦初始化,就不再是 nil 了!【例如上面的b被初始化了,fmt.Println(b == nil)的结果就是false】
		2.2.1 每种类型对应的零值如下:
		bool -> false
		numbers -> 0
		string -> ""
		pointers -> nil
		slice -> nil
		maps -> nil
		channels -> nil
		functions -> nil
		interfaces -> nil
	*/
	/*	3.切片的循环遍历
		切片的循环遍历和数组的循环遍历是一样的。可以使用for循环和for-range循环。
	*/
	// 3.1 for循环方法遍历
	var aa = []string{"北京", "上海", "深圳"}
	for i := 0; i < len(aa); i++ {
		fmt.Println("for循环打印切片aa的结果:", aa[i])
	}
	// 3.2 for-range方法遍历
	for index, value := range aa {
		fmt.Println("for-range循环打印切片aa的结果:", index, value)
	}
	// 4.基于数组定义切片
	// 由于切片的底层就是一个数组,所以我们可以基于数组定义切片。
	aaa := [5]int{55, 56, 57, 58, 59}
	bbb := aaa[1:4]
	fmt.Println(bbb)                        // [56 57 58]
	fmt.Printf("type of bbb is:%T \n", bbb) //	type of bbb is:[]int
	// 还支持如下方式
	ccc := aaa[1:]   // 从索引为1的下标开始一直取到末尾,包含末尾的值。
	ddd := aaa[:4]   // 从索引为0的下标开始取到索引为4的位置,不包含索引为4的值
	eee := aaa[:]    // 从头取到尾,取全部的值。
	fmt.Println(ccc) // [56 57 58 59]
	fmt.Println(ddd) // [55 56 57 58]
	fmt.Println(eee) // [55 56 57 58 59]
	// 5.切片再切片
	// 除了基于数组得到切片,我们还可以通过切片来得到切片。
	as := [...]string{"北京", "上海", "广州", "深圳", "成都", "重庆"}
	ab := as[1:3]
	fmt.Printf("结果:ab:%v type:%T len:%d cap:%d \n", ab, ab, len(ab), cap(ab)) // 结果:ab:[上海 广州] type:[]string len:2 cap:5
	ac := ab[1:5]
	fmt.Printf("结果:ac:%v type:%T len:%d cap:%d \n", ac, ac, len(ac), cap(ac)) // 结果:ac:[广州 深圳 成都 重庆] type:[]string len:4 cap:4
	// 注意:对切片进行再切片时,索引不能超过原数组的长度,否则会出现索引越界的错误。
	/*	6.关语切片的长度和容量
		6.1 切片拥有自己的长度和容量,我们可以通过使用内置的len()函数求长度,使用内置的cap()函数求切片的容量。
		6.2 切片的长度就是它所包含的元素个数。
		6.3 切片的容量是从它的第一个元素开始数,到其底层数组元素末尾的个数。
		示例如下:
	*/
	ss := []int{2, 3, 5, 7, 11, 13}
	fmt.Println(ss)                                    // [2 3 5 7 11 13]
	fmt.Printf("长度:%v 容量:%v \n", len(ss), cap(ss)) // 长度:6 容量:6
	cc := ss[:2]
	fmt.Println(cc)                                    // [2 3]
	fmt.Printf("长度:%v 容量:%v \n", len(cc), cap(cc)) // 长度:2 容量:6
	dd := ss[1:3]
	fmt.Println(dd)                                    // [3 5]
	fmt.Printf("长度:%v 容量:%v \n", len(dd), cap(dd)) // 长度:2 容量:5
	/*	7.切片的本质
		切片的本质就是对底层数组的封装,它包含了三个信息:底层数组的指针、切片的长度len和切片的容量cap
		这个具体的图片说明需要到自己的资料里面去看。图片在这里不好弄,故省略。
	*/
	/*	8.使用make()函数构造切片
		我们上面都是基于数组来创建的切片,如果需要动态的创建一个切片,我们就需要使用内置的make()函数。
		格式是:make([]T,size,cap),其中
		T:切片的元素类型
		size:切片中元素的数量
		cap:切片的容量
		8.1 举例说明如下:
			a := make([]int,2,10)
			fmt.Println(a)	// [0,0]
			fmt.Println(len(a))	// 2
			fmt.Println(cap(a))	// 10
		上面代码中a的内部存储空间已经分配了10个,但实际上只用了2个。容量并不会影响当前元素的个数,
		所以len(a)返回2,cap(a)则返回该切片的容量。
	*/
	/*	9.切片不能直接比较
		我们不能使用==操作符来判断两个切片是否含有全部相等元素。切片之间比较的话,会有这个报错,
		invalid operation: c == d (slice can only be compared to nil),
		告诉你切片只能和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的结果来判断。

	*/
	/*	10.切片是引用数据类型,注意切片的赋值拷贝
		下面的代码中演示了拷贝前后两个变量共享底层数组,对一个切片的修改会影响另一个切片的内容,这点需要特别注意。
		s1 := make([]int,3)	// [0 0 0]
		s2 := s1
		s2[0] = 100
		fmt.Println(s1)	// [100 0 0]
		fmt.Println(s2)	// [100 0 0]
	*/

	/*	11.append()方法为切片添加元素
		Go 语言的内建函数append()可以为切片动态添加元素,每个切片会指向一个底层数组,这个数组的容量够用就添加新增元素。
		当底层数组不能容纳新增的元素时,切片就会自动按照一定的策略进行扩容,每次扩容,新扩容的容量是上一次容量的2倍,
		此时该切片指向的底层数组就会更换。扩容操作往往发生在append()函数调用时,所以我们通常都需要用原变量接收append函数的返回值。
		11.1 给切片追加元素的错误写法:
			s3 := []int{1,2,3,4,5,6,7}
			s3[6] = 8
			fmt.Println(s3)	// index out of range [6] with length 6
		11.2 append()方法为切片追加元素的正确方法;
			append()添加元素和切片扩容,例子如下:
			var numSlice []int
			for 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:0xc0000a8000
			[0 1] len:2 cap:2 ptr:0xc0000a8040
			[0 1 2] len:3 cap:4 ptr:0xc0000b2020
			[0 1 2 3] len:4 cap:4 ptr:0xc0000b2020
			[0 1 2 3 4] len:5 cap:8 ptr:0xc0000b6000
			[0 1 2 3 4 5] len:6 cap:8 ptr:0xc0000b6000
			[0 1 2 3 4 5 6] len:7 cap:8 ptr:0xc0000b6000
			[0 1 2 3 4 5 6 7] len:8 cap:8 ptr:0xc0000b6000
			[0 1 2 3 4 5 6 7 8] len:9 cap:16 ptr:0xc0000b8000
			[0 1 2 3 4 5 6 7 8 9] len:10 cap:16 ptr:0xc0000b8000
			从上面的结果可以看出:
			1.append()函数将元素追加到切片的最后并返回该切片。
			2.切片numSlice的容量按照1,2,4,8,16这样的规则自动进行扩容,每次扩容后都是扩容前的2倍。
		11.3 append()函数还可以一次性的追加多个元素。例如:
			var citySlice []string
			// 追加一个元素
			citySlice = append(citySlice, "北京")
			// 追加多个元素
			citySlice = append(citySlice, "上海", "广州", "深圳")
			// 追加切片
			a := []string{"成都", "重庆"}
			citySlice = append(citySlice, a...)	// 注意:当append里面的两个参数都是切片的话,最后一个切片这个地方有个...
			fmt.Println(citySlice)	// [北京 上海 广州 深圳 成都 重庆]
		11.4 切片的追加切片
			s1 := []int{100,200,300}
			s2 := []int{400,500,600}
			s3 := append(s1, s2...)
			fmt.Println(s3)		// [100 200 300 400 500 600]

		12. 切片的扩容策略,记住新申请的容量是上一次容量的2倍就行。其他的仅作了解,具体的见学习文档即可。
	*/

	/*	13.使用copy()函数复制切片
		首先我们来看一个问题:
		a := []int{1,2,3,4,5}
		b := a
		fmt.Println(a)	// [1 2 3 4 5]
		fmt.Println(b)	// [1 2 3 4 5]
		b[0] = 1000
		fmt.Println(a)	// [1000 2 3 4 5]
		fmt.Println(b)	// [1000 2 3 4 5]
		由于切片是引用类型,所以a和b其实都是指向了同一个内存地址。a和b共用一个内存地址。修改b的同时a的值也会发生变化。同理,修改a的同时b的值也会发生变化。
		!!!注意!!!,Go语言中内建的copy()函数可以迅速地将一个切片的数据复制到另外一个切片空间中,他们在内存中分别有独立的内存地址,两者之间互不影响。
		copy()函数的格式:copy(destSlice, srcSlice []T)
		其中:
			1.srcSlice:数据来源切片
			2.destSlice:目标切片
		举个例子:
			// copy()复制切片
			a := []int{1,2,3,4,5}
			c := make([]int,5,5)
			copy(c,a)	// 使用copy()函数将切片a中的元素复制到切片c中
			fmt.Println(a)	// [1 2 3 4 5]
			fmt.Println(c)	// [1 2 3 4 5]
			c[0] = 1000
			fmt.Println(a)	// [1 2 3 4 5]
			fmt.Println(c)	// [1000 2 3 4 5]


	*/

	/*	14.从切片中删除元素
		Go 语言中并没有删除切片元素的专用方法,我们可以使用切片本身的特性来删除元素。代码如下:
		// 从切片中删除元素
		a := []int{30,31,32,33,34,35,36,37}
		// 要删除索引为2的元素
		a = append(a[:2],a[3:]...)
		fmt.Println(a)	// [30 31 33 34 35 36 37]
		总结一下就是:要从切片a中删除索引为index的元素,操作方法是a=append(a[:index],a[index+1:]...)

	*/

	s1 := []int{100, 200, 300}
	s2 := []int{400, 500, 600}
	s3 := append(s1, s2...)
	fmt.Println("切片的追加切片:", s3) // 切片的追加切片: [100 200 300 400 500 600]

	// 15.练习题,请写出下面代码的输出结果。
	var a3 = make([]string, 5, 10)
	fmt.Println("a3的初始化结果是:", a3) // a3的初始化结果是: [    ]
	for i := 0; i < 12; i++ {
		a3 = append(a3, fmt.Sprintf("%v", i))
	}
	fmt.Println("输出a3的最终结果:", a3, "a3的长度是:", len(a3), "a3的容量是:", cap(a3))
	// 输出a3的最终结果: [     0 1 2 3 4 5 6 7 8 9 10 11] a3的长度是: 17 a3的容量是: 20

	// *****************end****************
	/*	上面代码的全部执行结果如下:
		PS C:\Users\Zhaoshan.lu\go\DaDi> go run .\10.go
		求和结果是: 10
		[]
		[]
		[false true]
		true
		false
		false
		for循环打印切片aa的结果: 北京
		for循环打印切片aa的结果: 上海
		for循环打印切片aa的结果: 深圳
		for-range循环打印切片aa的结果: 0 北京
		for-range循环打印切片aa的结果: 1 上海
		长度:6 容量:6
		[2 3]
		长度:2 容量:6
		[3 5]
		长度:2 容量:5
		切片的追加切片: [100 200 300 400 500 600]
		a3的初始化结果是: [    ]
		输出a3的最终结果: [     0 1 2 3 4 5 6 7 8 9 10 11] a3的长度是: 17 a3的容量是: 20
		PS C:\Users\Zhaoshan.lu\go\DaDi>
	*/

}




标签:切片,nil,08,cap,len,Golang,Println,Go,fmt
From: https://www.cnblogs.com/ludundun/p/16951180.html

相关文章

  • Golang反射修改变量值
    1.前言前面的随笔Golang反射获取变量类型和值分享了如何通过反射获取变量的类型和值,也就是Golang反射三大定律中的前两个,即从interface{}到反射对象和从反射对象到inter......
  • MongoDB入门
    MongoDB数据库https://www.liuqingzheng.top/db/Mongodb系列/Mongodb快速入门/1MongoDB介绍#MongoDB数据库简单介绍是一个面对文档(document-oriented)的数据库,不......
  • MongoDB 的使用
    MongoDB的使用mongoDB的常见命令使用;MongoDB默认在27017端口上运行;1.常见命令#进入交互终端mongo开启云监控平台//开启云监控平台[公司一般不会允许内部信......
  • MongoDB 介绍及安装
    MongoDB介绍及安装官方文档:https://www.mongodb.com/docs/中文文档:https://www.mongodb.org.cn/操作文档:https://www.qikegu.com/docs/3267mongoDB的生态、理念非常先......
  • Python 使用MongoDB & MongoDB 工具的封装
    Python使用MongoDB补充:操作之前首先在虚拟机或者服务器端启动MongoDB;#重新加载配置,并启动mongodbsudosystemctldaemon-reloadsudosystemctlstartmongod#......
  • 2022-2023-1 20221408《计算机基础与程序设计》第十四周学习总结
    第十四周学习总结作业信息这个作业属于哪个课程:https://edu.cnblogs.com/campus/besti/2022-2023-1-CFAP这个作业的要求在哪里:https://www.cnblogs.com/rocedu/p/95778......
  • Django中使用django_table2如何加一列操作
    要加入列需要自定义一个列,doc中给出如下的例子:>>>fromdjango.utils.safestringimportmark_safe>>>fromdjango.utils.htmlimportescape>>>>>>classImageColu......
  • Golang抢占式调度
    Golang抢占式调度在1.12版本之前,go的调度器不支持抢占式调度,程序只能依靠Goroutine主动让出CPU资源才能触发调度,会引发一些问题,如某些Goroutine可以长时间占用线程,造成......
  • Golang 协程调度器原理及GPM模型
    目录进程和线程内核级线程用户级线程协程协程与线程的关系N:11:1M:Ngoroutine旧版本goroutine调度器调度器的实现Goroutine调度器的GMP模型设计思想GPM结构组成GPM运行模型......
  • Got a packet bigger than 'max_allowed_packet' bytes,cpp mysql insert multiple va
     YoucanalsologintoMySQLconsoleandrunthefollowingcommandtosetmax_allowed_packetsizeforthedurationofthesession.setglobalnet_buffer_leng......