Go语言学习、切片定义和使用

一、切片的定义和使用

package main

import "fmt"

func main() {
	//数组定义 var 数组名 [元素个数] 数据类型
	//切片定义 var 切片名 [] 数据类型
	var  s [] int
	fmt.Println(s)
}

输出结果

[]

可以看到,我们在上面的程序中,将切片的定义和数组的定义做了对比,切片的定义时,不需要任何元素个数

当定义完成切片后,我们做打印,发现切片为空,因没有任何数据

package main

import "fmt"

func main() {
	//数组定义 var 数组名 [元素个数] 数据类型
	//切片定义 var 切片名 [] 数据类型
	var  s [] int
	s[0] = 123 //这里直接给切片复制,会报错,因为切片的长度为零
	fmt.Println(s)
}

输出结果:

panic: runtime error: index out of range

goroutine 1 [running]:
main.main()
	D:/go_work/切片.go:9 +0x17

我们定义完切片后,直接通过切片下标进行赋值操作,会出现上述错误,因为这时定义的切片长度为0,不能直接赋值

我们通过下面的方法,进行切片的定义

package main

import "fmt"

func main() {
	//make([] 数据类型, 切片长度)
	s := make([] int , 5) //通过自动推导类型,创建一个切片,类型为int,长度为5
	//通过下标为切片赋值
	s[0] = 123
	s[1] = 456
	s[2] = 789
	fmt.Println(s)
 }

二、make方法,创建切片操作,输出结果:

[123 456 789 0 0]

我们在没有对切片的元素进行赋值时,该元素的结果为0,也会被打印。

通过len查看切片长度

	//通过len查看切片的长度
	fmt.Println(len(s))

结果:

5

是我们定义的长度。

三、切片的扩展:

package main

import "fmt"

func main() {
	//make([] 数据类型, 切片长度)
	s := make([] int , 5) //通过自动推导类型,创建一个切片,类型为int,长度为5
	//通过下标为切片赋值
	s[0] = 123
	s[1] = 456
	s[2] = 789
	s[3] = 111
	s[4] = 222
	//s[5] = 333 //我们不能直接给切片进行,越界赋值,会直接报越界错误
	//但是通过append,可以直接添加切片元素
	s = append(s, 333)
	fmt.Println(s)

 }

输出结果:

[123 456 789 111 222 333]

我们可以看到,通过,s[5]  = 333 这种方式,不能直接给切片进行扩展操作,如果想要给一个长度已满的切片进行赋值操作,则需要我们通过append方法,进行添加元素操作。

我们在看看,添加完后的切片长度:

fmt.Println(len(s))

结果为:6

也可以通过append,添加多个元素,需要在元素值后面用逗号隔开:

s = append(s, 333,444,555)

四、切片的遍历操作:

1、

package main

import "fmt"

func main() {
	//make([] 数据类型, 切片长度)
	s := make([] int , 5) //通过自动推导类型,创建一个切片,类型为int,长度为5
	//通过下标为切片赋值
	s[0] = 123
	s[1] = 456
	s[2] = 789
	s[3] = 111
	s[4] = 222
	// 切片的遍历
	for i:=0; i<len(s);i++{
		fmt.Println(s[i])
	}
 }

结果:

123
456
789
111
222

通过循环遍历出切片的值

2、

	for i,v:=range s{
		fmt.Println(i,v)
	}

结果:

0 123
1 456
2 789
3 111
4 222

五、切片初始化操作:

我们在不适用make创建切片的时候,可以直接对切片进行初始化操作。

package main

import "fmt"

func main() {
	//这种创建切片的方式,跟创建数组类似
	//不写元素个数的,叫切片,必须写元素个数的叫数组
	//切片与数组都是连续的存储空间,切片可以扩展,元素不能扩展。
	//切片在内存中存储的区域是堆区。数组的数据存储在内存中的栈区
	var s [] int = []int{1,2,3,4,5}
	fmt.Println(s)
	
}

结果:

[1 2 3 4 5]

六、切片的容量:

package main

import "fmt"

func main() {
	var s [] int = []int{1,2,3,4,5}
	fmt.Println(s)
	//切片的长度
	fmt.Println("长度 = ",len(s))
	// 切片的容量
	fmt.Println("容量 = ", cap(s))
}

结果:

[1 2 3 4 5]
长度 =  5
容量 =  5

可以看到,我们使用cap方法来获得切片容量大小

我们在s中,添加一个元素

package main

import "fmt"

func main() {
	var s [] int = []int{1,2,3,4,5}
	fmt.Println(s)
	//添加元素:
	s = append(s, 6,7,8,9)

	//切片的长度
	fmt.Println("长度 = ",len(s))
	// 切片的容量
	fmt.Println("容量 = ", cap(s))

	//添加一个元素到s中
	s = append(s, 10,11,12,13)
	//切片的长度
	fmt.Println("长度 = ",len(s))
	// 切片的容量
	fmt.Println("容量 = ", cap(s)) 
}

输出结果:

[1 2 3 4 5]
长度 =  9
容量 =  12
长度 =  13
容量 =  24

可以看到,容量一定是大于等于长度的,且容量的每次扩展是上一次容量的倍数。

当容量大小,超过当前容量,才会自动扩展。

如果整体数据,没有超过1024个字节,每次扩展为上一次的倍数,如果超过1024,则每次扩展是上次的四分之一。

举例:

package main

import "fmt"

func main() {
	 s := make([]int, 0 ,1) //容量为1
	 oldCap := cap(s)
	 for i:=0; i<20; i++{
	 	s = append(s,i)
	 	newCap:= cap(s)
	 	if oldCap < newCap{
	 		fmt.Printf("cap: %d========> %d\n", oldCap, newCap)
	 		oldCap = newCap
		}
	 }
}

输出结果:

cap: 1========> 2
cap: 2========> 4
cap: 4========> 8
cap: 8========> 16
cap: 16========> 32

通过上面这个例子,我们可以看到,是切片的扩容是通过2倍进行的

在看下,下面这个例子:

package main

import "fmt"

func main() {
	 s := make([]int, 0 ,1) //容量为1
	 oldCap := cap(s)
	 for i:=0; i<200000; i++{ //我们吧循环条件修改变大
	 	s = append(s,i)
	 	newCap:= cap(s)
	 	if oldCap < newCap{
	 		fmt.Printf("cap: %d========> %d\n", oldCap, newCap)
	 		oldCap = newCap
		}
	 }
}

结果:

cap: 512========> 1024  //两倍扩容
cap: 1024========> 1344 //不再是两倍扩容
cap: 1344========> 1696
cap: 1696========> 2368
cap: 2368========> 3072
cap: 3072========> 4096
cap: 4096========> 5120
cap: 5120========> 6816
cap: 6816========> 10240
cap: 10240========> 14336
cap: 14336========> 18432
cap: 18432========> 24576
cap: 24576========> 30720
cap: 30720========> 38912
cap: 38912========> 49152
cap: 49152========> 61440
cap: 61440========> 77824
cap: 77824========> 98304
cap: 98304========> 122880
cap: 122880========> 153600
cap: 153600========> 192512
cap: 192512========> 241664

当容量小于1024时是按照2倍容量扩容,当大于等于1024是不是按照2倍容量扩容。

七、切片的截取:

首先说一下切片的截取操作,所谓截取就是从切片中获取指定的数据。

    我们通过如下程序给大家解释一下:

package main

import "fmt"

func main() {
	//定义切片,并完成初始化
	s:= []int {10,20,30,40,50}

	// 从切片s中截取数据
	slice:= s[0:3:5]
	fmt.Println(slice)
}

结果:

[10 20 30]

s[0:3:5]是什么意思呢?

我们可以使用s[low:high:max]来表示

 第一个数(low)表示下标的起点(从该位置开始截取),如果low取值为0表示从第一个元素开始截取,也就是对应的切片s中的10

第二个数(high)表示取到哪结束,也就是下标的终点(不包含该位置),3表示取出下标是0,1,2的数据(10,20,30),不包括下标为3的数据,那么也就是说取出的数据长度是3. 可以根据公式:3-0 计算(len=high-low),也就是第二个数减去第一个数,差就是数据长度。在这里可以将长度理解成取出的数据的个数。

第三个数用来计算容量,所谓容量:是指切片目前可容纳的最多元素个数。通过公式5-0计算(cap=max-low),也就是第三个数据减去第一个数。该案例中容量为5

关于切片的截取还有其它的操作,如下图所示:

操作

含义

s[n]

切片s中索引位置为n的项

s[:]

从切片s的索引位置0到len(s)-1处所获得的切片

s[low:]

从切片s的索引位置low到len(s)-1处所获得的切片

s[:high]

从切片s的索引位置0到high处所获得的切片,len=high

s[low:high]

从切片s的索引位置low到high处所获得的切片,len=high-low

s[low:high:max]

从切片s的索引位置low到high处所获得的切片,len=high-low,cap=max-low

len(s)

切片s的长度,总是<=cap(s)

cap(s)

切片s的容量,总是>=len(s)

通过下面的例子,我们简单的演示一下:

1、

package main

import "fmt"

func main() {
	//定义切片,并完成初始化
	s:= []int {0,1,2,3,4,5,6,7,8,9}
	// 从切片s中截取数据
	slice:= s[:] //不指定容量和长度一样
	fmt.Println("slice = ",slice)
	fmt.Printf("len = %d, cap = %d\n", len(slice), cap(slice))
}

输出结果:

slice =  [0 1 2 3 4 5 6 7 8 9]
len = 10, cap = 10

2、

package main

import "fmt"

func main() {
	//定义切片,并完成初始化
	s:= []int {0,1,2,3,4,5,6,7,8,9}
	// 从切片s中截取数据
	slice:= s[3:] //从下标3开始
	fmt.Println("slice = ",slice)
	fmt.Printf("len = %d, cap = %d\n", len(slice), cap(slice))
}

结果:

slice =  [3 4 5 6 7 8 9]
len = 7, cap = 7

3、

package main

import "fmt"

func main() {
	//定义切片,并完成初始化
	s:= []int {0,1,2,3,4,5,6,7,8,9}
	// 从切片s中截取数据
	slice:= s[:6] //从下0开始,取6个元素,容量是10
	fmt.Println("slice = ",slice)
	fmt.Printf("len = %d, cap = %d\n", len(slice), cap(slice))
}

结果:

slice =  [0 1 2 3 4 5]
len = 6, cap = 10

4、

package main

import "fmt"

func main() {
	//定义切片,并完成初始化
	s:= []int {0,1,2,3,4,5,6,7,8,9}
	// 从切片s中截取数据
	slice:= s[2:5] //从下2开始(包含该元素),取到下标5为止(不包含该元素),切片长度为3,容量是8,10-2
	fmt.Println("slice = ",slice)
	fmt.Printf("len = %d, cap = %d\n", len(slice), cap(slice))
}

结果:

slice =  [2 3 4]
len = 3, cap = 8

八、copy函数使用

针对切片操作常用的方法除了append( )方法以外,还有copy方法.

基本语法:copy(切片1,切片2)

将第二个切片里面的元素,拷贝到第一个切片中。

下面通过一个案例,看一下该方法的使用:

package main

import "fmt"

func main() {
	s1 := []int{1,2}
	s2 := []int{4,5,6,7,8}
	copy(s2,s1) //将s1中的元素,拷贝到s2中
	fmt.Print(s2)
}

结果:

[1 2 6 7 8]

通过以上结果可以分析出,直接将1切片中两个元素拷贝到s2元素中相同的位置。而s2原有的元素备替换掉。

在看下面的:

package main

import "fmt"

func main() {
	s1 := []int{1,2}
	s2 := []int{4,5,6,7,8}
	copy(s1,s2) //将s2中的元素,拷贝到s1中
	fmt.Print(s1)
}

输出结果:

[4 5]

而,将s2拷贝到s1,则会吧s1中的元素替换掉。

今天就讲到这里。感谢各位阅读,欢迎点赞评论。谢谢!!

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章