前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Golang之旅7-切片slice

Golang之旅7-切片slice

作者头像
皮大大
发布2021-03-02 11:22:54
1920
发布2021-03-02 11:22:54
举报
文章被收录于专栏:机器学习/数据可视化

切片slice

  • 切片是引用类型切片初始化之后才能进行使用
  • 切片不能直接比较
  • append函数可以向切片中追加元素,同时还能利用append函数删除切片中的某个元素
  • 切片是数组的底层封装
  • make([]T, len,map)函数能够直接创建切片make([]int, 5, 10)

切片(Slice)是一个拥有相同类型元素可变长度的序列。切片是引用类型,必须初始化才能进行使用。它是基于数组类型做的一层封装。它非常灵活,支持自动扩容。 切片是一个引用类型,一般用于快速地操作一块数据集合。切片的底层就是数组,三要素:

  • 地址:指针的第一个位置
  • 长度:len()函数获得
  • 容量:cap()函数获得

切片的基本语法:

代码语言:javascript
复制
var name []T
  • name表示变量名
  • T:表示切片中的元素类型,比如string、int、bool
  • 切片创建的时候不指定长度;[]里面不带数字
代码语言:javascript
复制
func main() {
	// 声明切片类型
	var a []string              //声明一个空字符串切片
	var b = []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)   //切片是引用类型,不支持直接比较,只能和nil比较
}

GO切片

make函数

通过make()函数构造动态的切片,基本语法为

代码语言:javascript
复制
make([]T, size, cap)
  • T:切片的元素类型
  • size:切片中元素的数量
  • cap:切片的容量
代码语言:javascript
复制
//make函数构造切片
h := make([]int,5,10)  //5代表长度,10代表容量(可省略)
fmt.Println(h)
fmt.Printf("%T\n", h)

切片的本质

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

  • 指针
  • 长度
  • 容量

现在有个数组a := [8]int{0, 1, 2, 3, 4, 5, 6, 7},切片s1 := a[:5],相应示意图如下:

  • 指针:第一个元素所在的位置
  • 长度:元素总个数
  • 容量:原有数组的容量减去指针所在的位置

切片s2 := a[3:6]

切片不能直接比较

空切片nil

一个切片在未初始化之前默认都是nil,长度是0

代码语言:javascript
复制
package main

import "fmt"

func main(){
	var a []int     //定义切片未初始化
 	var b = []int{}  // 定义并且初始化

 	printSlice(a)
	//判断a和nil的关系
	if (a== nil){
		fmt.Println("切片a是空的")
	}

	printSlice(b)
	//判断b和nil的关系
	if (b == nil){
		fmt.Println("切片b是空的")    //b已经初始化,索引不会打印本句
	}
}

//定义printSlice函数,输出长度、容量和切片
func printSlice(x []int){
	fmt.Printf("len=%d cap=%d slice=%v\n", len(x), cap(x), x)
}

切片赋值拷贝

当进行赋值拷贝之后,两个变量共享底层数组的数据,一个切片的变化会影响另一个切片。

代码语言:javascript
复制
package main

import "fmt"

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]
}

遍历

切片的遍历分为两种:索引遍历和for range遍历

代码语言:javascript
复制
package main

import "fmt"

//切片遍历
func main(){
	numArray := []int{1,2,3,4,5,6,7,8}
	
    //索引遍历
	for i :=0; i<len(numArray); i++{
		fmt.Println(i, numArray[i])
	}

	fmt.Println()
	
    //for range 遍历
	for index, value := range numArray{
		fmt.Println(index, value)
	}
}

append

append函数可以为切片动态添加元素。每个切片指向一个底层数组,数组能够容纳一个数量的元素。当底层数组不能容纳新增的元素时,切片就会自动按照一定的策略进行“扩容”,此时该切片指向的底层数组就会更换。“扩容”操作往往发生在append()函数调用时。

代码语言:javascript
复制
package main

import "fmt"

func main(){
	var a []int      //此时a没有申请内存,不能直接添加元素
	//a[0] = 100
	//fmt.Println(a)

	for i :=0; i<10; i++{
		a = append(a,i)  // append函数将元素追加到切片的最后,同时需要原来的切片进行接收
		fmt.Printf("%v len:%d cap:%d ptr:%p\n", a, len(a), cap(a), a)
	}

	//append追加多个元素
	var citySlice []string
	citySlice = append(citySlice, "北京")   // 追加一个元素
	citySlice = append(citySlice, "上海", "广州", "深圳")  	// 追加多个元素
	b := []string{"成都", "重庆"}
	citySlice = append(citySlice, b...)   	// 追加切片
	fmt.Println(citySlice) //[北京 上海 广州 深圳 成都 重庆]
}
代码语言:javascript
复制
[0] len:1 cap:1 ptr:0xc00000e100
[0 1] len:2 cap:2 ptr:0xc00000e150
[0 1 2] len:3 cap:4 ptr:0xc00000a3a0
[0 1 2 3] len:4 cap:4 ptr:0xc00000a3a0
[0 1 2 3 4] len:5 cap:8 ptr:0xc00000c280
[0 1 2 3 4 5] len:6 cap:8 ptr:0xc00000c280
[0 1 2 3 4 5 6] len:7 cap:8 ptr:0xc00000c280
[0 1 2 3 4 5 6 7] len:8 cap:8 ptr:0xc00000c280
[0 1 2 3 4 5 6 7 8] len:9 cap:16 ptr:0xc00008c000
[0 1 2 3 4 5 6 7 8 9] len:10 cap:16 ptr:0xc00008c000

小结

  • append函数将元素追加到切片的最后,同时需要原来的切片进行接收
  • 容量按照1,2,4,8.16进行扩容,每次扩容后都是扩容前的2倍。
  • append函数能够同时追加多个元素

切片的扩容策略

代码语言:javascript
复制
newcap := old.cap
doublecap := newcap + newcap
if cap > doublecap {
	newcap = cap   //新申请的容量cap > 2倍旧容量,最终的容量newcap就是新申请的容量cap
} else {
	if old.len < 1024 {  
		newcap = doublecap   //如果旧切片的长度小于1024,最终容量就是旧容量的2倍
	} else {
		// Check 0 < newcap to detect overflow
		// and prevent an infinite loop.
		for 0 < newcap && newcap < cap {  //如果旧切片的长度大于1024,则新容量每次增加旧容量的四分之一,直到最终容量大于等于新申请的容量
			newcap += newcap / 4
		}
		// Set newcap to the requested cap when
		// the newcap calculation overflowed.
		if newcap <= 0 {
			newcap = cap  //如果最终容量溢出,则最终容量就是新申请的容量
		}
	}
}

copy函数

copy函数实现将一个切片的数据迅速地复制到另一个切片空间中,切片要初始化之后才能使用。基本语法为:

代码语言:javascript
复制
copy(destSlice, srcSlice []T)
  • srcSlice: 数据来源切片
  • destSlice: 目标切片
代码语言:javascript
复制
package main

import "fmt"

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]

	a := []int{1,2,3,4,5}
	b := make([]int, 5, 5)
	c := b     //b直接赋值给c,二者共享数组中的数据,变化同时发生。
	copy(b,a)  //ab之间没有关系,独立
	b[0] = 20
	fmt.Println(a)
	fmt.Println(b)
	fmt.Println(c)

	//通过copy函数删除元素
	d := []string{"北京","深圳","广州","长沙"}
	d = append(d[0:2], d[3:]...)
	fmt.Println(d)
}

总结:删除切片中索引为index的元素,需要注意的是append函数中可以同时追加多个元素

代码语言:javascript
复制
append(a[0:index], a[index+1:]...)

练习题

代码语言:javascript
复制
package main

import (
	"fmt"
	"sort"
)

func main() {
	var a = make([]string, 5, 10)   //定义初始化[     ]  5个空格的空切片
	fmt.Println(a)
	for i := 0; i < 10; i++ {
		a = append(a, fmt.Sprintf("%v", i))
	}
	fmt.Println(a)
	fmt.Printf("%T\n", a)  //[]string类型的切片
	fmt.Println(len(a))  //15


	//请使用内置的sort包对数组var b = [...]int{3, 7, 8, 9, 1}进行排序
	var b = [...]int{4,8,2,9,1}   //定义数组
	sort.Ints(b[:])   //将数组变成了切片,直接排序
	fmt.Println("sort_b:",b)
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-9-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 切片slice
  • make函数
  • 切片的本质
  • 切片不能直接比较
  • 空切片nil
  • 切片赋值拷贝
  • 遍历
  • append
  • 切片的扩容策略
  • copy函数
  • 练习题
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档