• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

Go语言基础切片slice的声明和使用

武飞扬头像
我是沐风晓月
帮助1

前言

大家好,我是沐风晓月,本文go语言入门-掌握go语言函数收录于《go语言学习专栏》专栏,此专栏带你从零开始学习go语言,持续更新中,欢迎点赞收藏。

学新通

一. 切片的定义

1.1 切片的定义和特点

Go语言中的切片,是一种动态数组,它是对数组的引用,切片本身并不存储任何数据,它只是描述了数组中的一段。

切片有如下几个特点:

  • 与数组相⽐,切⽚的⻓度是不固定的,可以追加元素,在追加时可能使切⽚的容量增⼤。
  • 切⽚本身没有任何数据,它们只是对现有数组的引⽤。
  • 切⽚与数组相⽐,不需要设定⻓度,在[]中不⽤设定值,相对来说⽐较⾃由。
  • 切片是引用类型,默认值为nill
  • 切片的索引也是从零开始
  • 切片的遍历方式与数组相同

从概念上⾯来说slice像⼀个结构体,这个结构体包含了三个元素:

1)指针,指向数组中slice指定的开始位置;
2)长度,即slice的当前⻓度;
3)容量,也就是slice所容纳的最大元素个数。

1.2 切片的结构

切片是一种数据类型,这种数据类型便于使用和管理数据集合。

首先我们需要明白切片的结构。slice在Go的运行时库中就是一个C语言动态数组的实现:

type slice struct {
    ptr *array  //底层存储数组
    len int     //当前存储了多少个元素
    cap int     //底层数组可以存储多少个元素(从ptr指向的位置开始)
}

这个结构有3个字段,第一个字段表示array的指针,就是真实数据的指针(这个一定要注意),第二个是表示slice的长度,第三个是表示slice的容量,特别需要注意的是:

1.slice的长度和容量都不是指针
2.切片操作并不复制切片指向的元素。它创建一个新的切片并复用原来切片的底层数组。

切片操作并不复制切片指向的元素。它创建一个新的切片并复用原来切片的底层数组。 使得切片操作和数组索引一样高效。因此,通过一个新切片修改元素会影响到原始切片的对应元素。

切片的本质就是对底层数组的封装,它包含了三个信息:底层数组的指针、切片的长度(len)和切片的容量(cap)。

学新通
这 3 个字段分别是指向底层数组的指针、切片访问的元素的个数(即长度)和切片允许增长到的元素个数(即容量)。

二. 切片的声明和初始化

2.1 声明切片

1. 声明一个未指定长度的数组来定义切片

语法:

var slicename []type
  • slicename : 表示变量名

需要注意的是: 该声明⽅式中未初始化的切⽚为空切⽚。该切⽚默认为 nil,⻓度为 0。

package main

import "fmt"

func main() {
	var a []string  //声明一个字符串切片
	var b = []int{} // 声明一个整数切片并初始化

	fmt.Println(a, b, a == nil) // 执行结果 [] [] true


}

2. 使用make()的方式声明切片

语法:

var slice1 []type = make([]type, len, cap)
//type 表示切片的元素类型
//len 表示切片中元素的数量
//cap 表示切片的最大容量

可进行缩写:

- var slice1 []type = make([]type, len)
- 可以简写为: slice1 := make([]type, len)

案例一:使用make直接定义切片

package main

import "fmt"

func main() {
	var mufeng2 []int = make([]int, 5, 7)
	var mufeng2 []int = make([]int, 5) // 缩写

	fmt.Println(mufeng2)

}

//执行结果: [0 0 0 0 0]

案例二:使用make定义并获相应的len,cap

package main

import "fmt"

func main() {
	var mufeng = make([]int, 1, 6)
	fmt.Println(mufeng)
	fmt.Println(len(mufeng), cap(mufeng), mufeng)
	fmt.Printf("len=%d cap=%d,slice=%v\n", len(mufeng), cap(mufeng), mufeng)
}

执行结果:

[0]
1 6 [0]              
len=1 cap=6,slice=[0]

len : 表示长度
cap: 表示容量

容量可以省略不写,若不写默认与len相同。

使用make() 定义的时候,系统会初始化为0,而不是nil。之所以 不是nil ,是因为make 函数为其 分配了内存空间。

2.2 初始化切片

1. 直接初始化切片

package main

import "fmt"

func main() {
	mufeng := []int{1, 2, 3}  //由系统自动创建底层数组
	fmt.Println(mufeng)
}
//执行结果: [1 2 3]

2. 基于数组来截取初始化切片

package main

import "fmt"

func main() {
	//定义数组:
	mufeng := [5]int{1, 2, 3, 4, 5}
	//定义切片:

	s := mufeng[:] //切片中包含数组的所有数据

	s1 := mufeng[2:4] 
	s2 := mufeng[1:] //从下标1到最后
	s3 := mufeng[:3]// 从最开始到下标为3-1

	fmt.Println(mufeng, s, s1, s2, s3)
	//执行结果:
}

学新通

执行结果:

[1 2 3 4 5] [1 2 3 4 5] [3 4] [2 3 4 5] [1 2 3]

代码详解:

定义数组mufeng := [5]int{1, 2, 3, 4, 5}

  1. 切片中包含数组中所有的数据: s := mufeng[:]
  2. s := mufeng[startIndex:endIndex]表示从startIndex 到endIndex :
s1 := mufeng[2:4]

这里的[]是一个前闭后开区间

比如这这个例子中,这里的startIndex就是2,endIndex就是4, 所以s1 := mufeng[2:4] 就是获取下标为2到4的值,结果就是[3, 4], 但这里不包含下标4

[2:4] == 2<=s1<4

总结:

操作 含义
s[n] 切片中索引位置为n的项
s[:] 从数组索引为0的位置到len(s)-1的位置处所获得切片
s[n1:] 从数组索引为n1的位置到len(s)-1处所获得的切片
s[:n2] 从数组索引为0的位置到n2处所获得的切片
s[n1:n2:max] 从索引n1位置到n2处所获得的切片。
len(s) 切片s的长度
cap(s) 切片s的容量

三. 为切片元素赋值

3.1 添加元素 append() 方法

Go语言的内建函数 append() 可以为切片动态添加元素:

package main

import "fmt"

func main() {
	var mufeng []int
	mufeng = append(mufeng, 1)                   //追加一个元素
	mufeng1 := append(mufeng, 2, 3, 4)           //追加多个元素
	mufeng2 := append(mufeng, []int{1, 2, 3}...) //追加一个切片

	fmt.Println(mufeng, mufeng1, mufeng2)
}

输出结果:

[1] [1 2 3 4] [1 1 2 3]

这里需要注意: append() 在为切片去动态添加元素的时候,如果空间不足以容纳足够多的元素的时候,切片就会进行扩容。切片在扩容时,容量的扩展规律是按容量的 2 倍数进行扩充,例如 1、2、4、8、16等倍数扩容。

package main

import "fmt"

func main() {
	var mufeng []int

	for num := 0; num < 10; num   {
		mufeng = append(mufeng, num)
		fmt.Printf("len:%d cap:%d,pointer:%p\n", len(mufeng), cap(mufeng), mufeng)

	}
}

执行结果:

学新通

3.2 切片拷贝 copy()方法

Go语言的内置函数 copy() 可以将一个数组切片复制到另一个数组切片中,如果加入的两个数组切片不一样大,就会按照其中较小的那个数组切片的元素个数进行复制。copy() 函数的使用格式如下:

copy( destSlice, srcSlice []T) int

// 其中 srcSlice 为数据来源切片
// destSlice 为复制的目标(也就是将 srcSlice 复制到 destSlice)
// 目标切片必须分配过空间且足够承载复制的元素个数,并且来源和目标的类型必须一致
// copy() 函数的返回值表示实际发生复制的元素个数。

案例一

package main

import "fmt"

func main() {

	mufeng1 := []int{1, 2, 3, 4, 5}
	mufeng2 := []int{5, 6, 7}

	a := copy(mufeng1, mufeng2)
	b := copy(mufeng2, mufeng1)

	fmt.Println(a, b) //这里返回的是复制的总元素个数 3,3
	fmt.Println(mufeng1, mufeng2) //[5 6 7 4 5] [5 6 7]

}
学新通

复制的流程: mufeng1 复制到mufeng2 :

mufeng2前三个元素{5,6,7} 占位,只需要把mufeng1{4,5}复制过来即可,也就是:{5,6,7,4,5}

3.3 从切片中删除元素

Go语言并没有对删除切片元素提供专用的语法或者接口,需要使用切片本身的特性来删除元素,根据要删除元素的位置有三种情况,分别是从开头位置删除、从中间位置删除和从尾部删除,其中删除切片尾部的元素速度最快。

  1. 从开头位置删除
package main

import "fmt"

func main() {

	mufeng1 := []int{1, 2, 3, 4, 5}
	mufeng1 = mufeng1[1:] //删除开头的一个元素
	fmt.Println(mufeng1) //[2 3 4 5]

	mufeng1 = mufeng1[3:] //删除开头的三个元素
	fmt.Println(mufeng1) //【5】
}

  1. 删除第二个元素
package main

import "fmt"

func main() {

	mufeng1 := []int{1, 2, 3, 4, 5}
	fmt.Println(mufeng1)
	mufeng1 = append(mufeng1[:1], mufeng1[1 1:]...)
	fmt.Println(mufeng1)
}

执行结果:

[1 2 3 4 5]
[1 3 4 5]

  1. 截取法

这里利用对 slice 的截取删除指定元素。注意删除时,后面的元素会前移,所以下标 i 应该左移一位。

上面的第二种方法也是这种方法,其实需要自己指定要删除的下标,接下来我们用for循环实现:

import "fmt"

func DeleteSlice(a []int, elem int) []int {

	for i := 0; i < len(a); i   {

		if a[i] == elem {
			a = append(a[:i], a[i 1:]...)
			i--
		}
	}
	return a
}

func main() {

	x := DeleteSlice([]int{1, 2, 3, 4, 5}, 2)
	fmt.Println(x) //[1 3 4 5]

}
学新通

这种方法修改了原来的切片: 切片是一个有三个字段的结构体,分别是: 地址,长度和容量, 这还总方法改变的是地址和长度,但是容量没有变:

package main

import "fmt"

func DeleteSlice(a []int, elem int) []int {

	for i := 0; i < len(a); i   {

		if a[i] == elem {
			a = append(a[:i], a[i 1:]...)
			i--
		}
	}
	return a
}

func main() {

	p := []int{1, 2, 3, 4, 5}
	fmt.Println(p, len(p), cap(p))
	fmt.Printf("address of p %p", &p)

	x := DeleteSlice(p, 2)
	fmt.Println(x) //[1 3 4 5]

	fmt.Println(len(x), cap(x))
	fmt.Printf("address of x %p", &x)
}

学新通

执行结果:

C:\Users\Administrator\AppData\Local\Temp\GoLand\___go_build_test_hello_go.exe
[1 2 3 4 5] 5 5
address of p 0xc000008078
[1 3 4 5]
4 5                               
address of x 0xc0000080a8      

总结

切片这边比较难,建议每隔一段时间就回来复习。

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgfiaje
系列文章
更多 icon
同类精品
更多 icon
继续加载