教程集 www.jiaochengji.com
教程集 >  Golang编程  >  golang教程  >  正文 Golang中切片的定义、使用

Golang中切片的定义、使用

发布时间:2021-04-09   编辑:jiaochengji.com
教程集为您提供Golang中切片的定义、使用等资源,欢迎您收藏本站,我们将为您提供最新的Golang中切片的定义、使用资源

1 Go语言中切片 slice

切片(Slice)是Go语言类型系统中的一种基本的数据类型,类似于C 中的vector,都是围绕动态数组概念构建的,可以按需自动增长和缩小。但是切片本身并不是动态数组,而是对底层数组的抽象。

切片有些类似于其他语言中的数组,但是有一些不同寻常的特性。

因为数组的长度是固定的并且数组长度属于类型的一部分,所以数组有很多的局限性。

1.1 切片的声明

两种声明切片类型的语句:
第一种

var sliceName []V
//其中 sliceName 是变量名
//V 表示切片中元素的类型

第二种

// 使用make()
make([]V, size, cap)
//V 表示切片的元素类型
//size 表示切片中元素的数量
//cap 表示切片的最大容量

func main() {
	// 声明切片类型
	var a []string //声明一个字符串切片
	var b = []int{} //声明一个整型切片并初始化
	//使用make()
	var slice2 []int = make([]int, 5, 7)  //长度为5 ,容器最大的容量是7的切片,变量名是slice2
}  

两种声明方法的对比

使用make()声明 已经在内存中声明了一个空间 , 而不需要临时申请。这样就导致了make()方法 导致内存浪费 ,浪费的内存不能被gc回收。所以,如果使用make()初始分配了一块空间,这样就不会再容量不够的时候就分配新空间,效率更高。

1.2 切片的长度和容量

切片拥有自己的长度和容量,我们可以通过使用内置的 len()函数求长度,使用内置的cap()函数求切片的容量。

由于切片的底层就是一个数组,所以我们可以基于数组定义切片。

注意:切片的截取是左闭右开的。(什么是左闭右开?见下c和d)

func main() {
	// 基于数组定义切片
	a := [5]int{1,2,3,4,5}
	//把b声明为从a种第一个元素到第四个元素的新切片。
	b := a[1:4] //基于数组a创建切片,包括元素a[1],a[2],a[3]
	fmt.Println(b) //[56 57 58]
	fmt.Printf("type of b:%T\n", b) //type of b:[]int
}
/* 还支持如下方式:
	c := a[1:] //[2 3 4 5]
	d := a[:4] //[1 2 3 4]
	e := a[:]  //[1 2 3 4 5]
*/

2 Go语言中切片类型出现的原因

切片是一种数据类型,这种数据类型便于使用和管理数据集合。
创建一个100万个int类型元素的数组,并将它传递给函数,将会发生什么?

一道面试真题 最后输出的 s 会是什么?
func main() {
    s := []int{1, 2, 3}                          
    ss := s[1:]                                        
    ss = append(ss, 4)

    for _, v := range ss {
        v  = 10
    }
    for i := range ss {
        ss[i]  = 10
    }
    fmt.Println(s) //
}

2.1 切片的结构

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

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

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

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

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

切片的本质

切片的本质就是对底层数组的封装,它包含了三个信息:底层数组的指针、切片的长度(len)和切片的容量(cap)。
举个例子,现在有一个数组a := [8]int{0, 1, 2, 3, 4, 5, 6, 7},切片s1 := a[:5],相应示意图如下。


切片s2 := a[3:6],相应示意图如下:

3 切片不能直接比较

切片之间是不能比较的,我们不能使用==操作符来判断两个切片是否含有全部相等元素。 切片唯一合法的比较操作是和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来判断。

4 切片的赋值拷贝

下面的代码中演示了拷贝前后两个变量共享底层数组,对一个切片的修改会影响另一个切片的内容,这点需要特别注意。

func main() {
	s1 := make([]int, 3) //[0 0 0]
	s2 := s1             //将s1直接赋值给s2,s1和s2共用一个底层数组
	s2[0] = 100
	fmt.Println(s1) //[100 0 0]
	fmt.Println(s2) //[100 0 0]
}

5 切片遍历

切片的遍历方式和数组是一致的,支持索引遍历和for range遍历。

func main() {
	s := []int{1, 3, 5}

	for i := 0; i < len(s); i   {
		fmt.Println(i, s[i])
	}

	for index, value := range s {
		fmt.Println(index, value)
	}
}

6 append()

append()可以为切片动态添加元素,每个切片会指向一个底层数组,这个数组的容量够用就添加新增元素。当底层数组不能容纳新增的元素时,切片就会自动按照一定的策略进行“扩容”,此时该切片指向的底层数组就会更换。“扩容”操作往往发生在append()函数调用时,所以我们通常都需要用原变量接收append函数的返回值。

举个例子:

func main() {
	//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)
	}
}

7 copy()

问题:

func main() {
	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]
}

由于切片是引用类型,所以ab其实都指向了同一块内存地址。修改b的同时a的值也会发生变化。

copy()函数可以迅速地将一个切片的数据复制到另外一个切片空间中,copy()函数的使用格式如下:

copy(destSlice, srcSlice []T)
// srcSlice: 数据来源切片
// destSlice: 目标切片

简单的例子:

func main() {
	// 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]
}

8 删除元素

使用切片本身的特性来删除元素。

func main() {
	// 从切片中删除元素
	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:]...)

到此这篇关于“Golang中切片的定义、使用”的文章就介绍到这了,更多文章或继续浏览下面的相关文章,希望大家以后多多支持JQ教程网!

您可能感兴趣的文章:
[go语言]-slice实现的使用和基本原理
golang 内置函数built-in
连nil切片和空切片一不一样都不清楚?那BAT面试官只好让你回去等通知了
golang切片内存应用技巧
切片 里面包含interface_Golang数据结构详解之切片
Golang的数组与切片——详解
Golang面试题总结
golang slice 最后一个元素_Go 常见的数据结构 Slice
Golang 数组和切片介绍
Go源码剖析:内置类型

[关闭]
~ ~