首页 > 其他分享 >切片基础篇

切片基础篇

时间:2022-08-29 07:56:18浏览次数:59  
标签:s1 cap 基础 len 切片 Println fmt

1. 定义

切片(Slice)是一个拥有相同类型元素的可变长度的序列。它是基于数组类型做的一层封装。它非常灵活,支持自动扩容。

切片是一个引用类型,它的内部结构包含地址长度容量。切片一般用于快速地操作一块数据集合。

var sint []int  // 定义一个int类型的切片
var sstr []string  // 定义一个string类型切片
fmt.Println(sint, sstr)
fmt.Println(sint == nil, sstr == nil)//nil ,其实没有分配内存的,初始化才能使用!数组有默认值!

out

[] []
true true

从上面可以看到,切片和数组直观上的不同

  • 切片定义的时候是没有长度的,而数组必须有长度,且是数组类型的一部分
  • 切片没有默认值,而数组是有默认值的
  • 切片可可以使用make()初始化,数组不可以

2. 切片初始化

sint = []int{1, 2, 3}
sstr = []string{"江宁", "雨花台", "鼓楼", "栖霞", "建邺"}
fmt.Println(sint)
fmt.Println(sstr)

out

[1 2 3]
[江宁 雨花台 鼓楼 栖霞 建邺]

3. 初识len()和cap()

切片的容量是底层数组从切片的第一个元素指向s数组的最后元素的这个长度。和长度是不一样的

fmt.Println(len(sint), cap(sint))
fmt.Println(len(sstr), cap(sstr))

out

3 3
5 5

4. 由数组得到切片

这个和python的是类似的

aint := [...]int{1, 3, 5, 7, 9, 11, 13, 15}
s1 := aint[0:4] // 闭右开,类似py里的列表
s2 := aint[1:6]
s3 := aint[:4]
s4 := aint[3:]
s5 := aint[:]
fmt.Printf("s1=%v\n", s1)
fmt.Printf("s2=%v\n", s2)
fmt.Printf("s3=%v\n", s3)
fmt.Printf("s4=%v\n", s4)
fmt.Printf("s5=%v\n", s5)

out

s1=[1 3 5 7]
s2=[3 5 7 9 11]
s3=[1 3 5 7]
s4=[7 9 11 13 15]
s5=[1 3 5 7 9 11 13 15]

5. 切片的长度和容量

切片的容量是底层数组从切片的第一个元素指向数组的最后元素的这个长度。和长度是不一样的

// 接上一段代码
fmt.Printf("len(s1)=%d, cap(s1)=%d\n", len(s1), cap(s1))
fmt.Printf("len(s4)=%d, cap(s4)=%d\n", len(s4), cap(s4))
fmt.Printf("len(s5)=%d, cap(s5)=%d\n", len(s5), cap(s5))
fmt.Printf("len(s3)=%d, cap(s3)=%d\n", len(s3), cap(s3))

out

len(s1)=4, cap(s1)=8
len(s4)=5, cap(s4)=5
len(s5)=8, cap(s5)=8
len(s3)=4, cap(s3)=8

6. 切片再切片

这种情况下,切片的容量计算方式也是一样的。

s6 := s2[0:3]
fmt.Printf("s6=%v\n", s6)
fmt.Printf("len(s6)=%d,cap(s6)=%d\n", len(s6), cap(s6))

out

s6=[3 5 7]
len(s6)=3,cap(s6)=7

7. 切片是引用类型

切片是引用类型,都指向了底层的一个数组,底层变了,它也变

// 接上面代码
aint[2] = 1300
fmt.Printf("s6=%v\n", s6)

out

s6=[3 1300 7]

8. 使用make()函数构造切片

make([]T, size, cap)

其中:

  • T:切片的元素类型
  • size:切片中元素的数量
  • cap:切片的容量

make 初始化,是分配内存的,不为nil,go语言毕竟偏向底层,指针,长度,容量,后面会用的多!

s1 := make([]int, 5, 10)  // 5是长度,10是容量
s2 := make([]int, 0, 10)  // 底层数组的长度是10
fmt.Printf("s1=%v,len(s1)=%d,cap(s1)=%d\n", s1, len(s1), cap(s1))
fmt.Printf("s2=%v,len(s2)=%d,cap(s2)=%d\n", s2, len(s2), cap(s2))

out

s1=[0 0 0 0 0],len(s1)=5,cap(s1)=10
s2=[],len(s2)=0,cap(s2)=10

9. 共享底层数组

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

s3 := []int{1, 3, 5}
s4 := s3  // 切片的赋值,s2和s4都指向了同一个底层数组,也就是同一个内存地址,本身是不存放数据的
fmt.Println(s3, s4)
s3[0] = 100
fmt.Println(s3, s4)

out

[1 3 5] [1 3 5]
[100 3 5] [100 3 5]

10. 遍历

切片的遍历和数组遍历是一样的,都可以通过索引或是for range的方式进行遍历

  • 索引遍历
for i := 0; i < len(s3); i++ {
		fmt.Println(s3[i])

	}
  • for range 遍历
for _, v := range s3 {
		fmt.Println(v)
	}

11. append()函数扩容切片

Go语言的内建函数append()可以为切片动态添加元素。 可以一次添加一个元素,可以添加多个元素,也可以添加另一个切片中的元素(后面加…)。

s1 := []string{"江宁", "鼓楼", "雨花台"}
fmt.Printf("s1=%v,len(s1)=%d,cap(s1)=%d\n", s1, len(s1), cap(s1))
// 调用append函数必须使用原来的切片变量接收返回值
s1 = append(s1, "建邺")
fmt.Printf("s1=%v,len(s1)=%d,cap(s1)=%d\n", s1, len(s1), cap(s1))
//添加多个
s1 = append(s1, "栖霞", "连云港")
fmt.Printf("s1=%v,len(s1)=%d,cap(s1)=%d\n", s1, len(s1), cap(s1))
//添加切片
ss := []string{"北京", "上海", "广州"}
s1 = append(s1, ss...)
fmt.Printf("s1=%v,len(s1)=%d,cap(s2)=%d\n", s1, len(s1), cap(s1))

out

s1=[江宁 鼓楼 雨花台],len(s1)=3,cap(s1)=3
s1=[江宁 鼓楼 雨花台 建邺],len(s1)=4,cap(s1)=6                             
s1=[江宁 鼓楼 雨花台 建邺 栖霞 连云港],len(s1)=6,cap(s1)=6 

append 追加元素,原来的底层数组放不下的时候,go底层会把底层数组换一个

上面的cap可以看到,s1的容量第一次追加后扩容到6,但后面再追加就没有变。再在此基础上,增加1个元素,容量扩容到12

/*
1、如果小于1024,2倍扩容
2、如果大于1024,1.25扩容
3、如果申请的容量大于原来的2倍,直接扩容到新申请的容量
4、具体存储的值类型不同,扩容的策略也不同
*/
s1 = append(s1, "bowen")
fmt.Printf("s1=%v,len(s1)=%d cap(s1)=%d\n", s1, len(s1), cap(s1))

out

s1=[江宁 鼓楼 雨花台 建邺 栖霞 连云港 北京 上海 广州 bowen],len(s1)=10 cap(s1)=12

12. append扩容策略

*容量扩容策略,根据不同的数据类型策略不一样。可以看源码,*$GOROOT/*src/runtime/slice.go*其中扩容相关代码如下

newcap := old.cap
doublecap := newcap + newcap
if cap > doublecap {
	newcap = cap
} else {
	if old.len < 1024 {
		newcap = doublecap
	} else {
		// Check 0 < newcap to detect overflow
		// and prevent an infinite loop.
		for 0 < newcap && newcap < cap {
			newcap += newcap / 4
		}
		// Set newcap to the requested cap when
		// the newcap calculation overflowed.
		if newcap <= 0 {
			newcap = cap
		}
	}
}

从上面的代码可以看出以下内容:

  • 首先判断,如果新申请容量(cap)大于2倍的旧容量(old.cap),最终容量(newcap)就是新申请的容量(cap)。
  • 否则判断,如果旧切片的长度小于1024,则最终容量(newcap)就是旧容量(old.cap)的两倍,即(newcap=doublecap),
  • 否则判断,如果旧切片长度大于等于1024,则最终容量(newcap)从旧容量(old.cap)开始循环增加原来的1/4,即(newcap=old.cap,for {newcap += newcap/4})直到最终容量(newcap)大于等于新申请的容量(cap),即(newcap >= cap)
  • 如果最终容量(cap)计算值溢出,则最终容量(cap)就是新申请容量(cap)。

需要注意的是,切片扩容还会根据切片中元素的类型不同而做不同的处理,比如intstring类型的处理方式就不一样。

13. copy函数

Go语言内建的copy()函数可以迅速地将一个切片的数据复制到另外一个切片空间中,注意是新开辟的空间,和第9小节区的共享底层数组分开来

s1 := []int{1, 3, 5}
s2 := s1 // 共享底层数组

var s3 = make([]int, 3, 3)
copy(s3, s1) // 重新开辟空间
fmt.Println(s1, s2, s3)
s1[1] = 100
fmt.Println(s1, s2, s3)

out

[1 3 5] [1 3 5] [1 3 5]
[1 100 5] [1 100 5] [1 3 5]

14. 刪除元素

Go语言中并没有删除切片元素的专用方法,我们可以使用切片本身的特性来删除元素。 代码如下:

// 接上面代碼
s1 = append(s1[:1], s1[2:]...)  // s1[2:] 换成s2[2:],也是可以成功删除的
fmt.Println(s1)

out

[1 5]

可以看到,s1第二个元素被删除了。总结一下就是:要从切片a中删除索引为index的元素,操作方法是a = append(a[:index], a[index+1:]...),注意不要写反,写反就不是删除,而是增加了。

补充:删除元素后,底层数组的变化

x1 := []int{1, 3, 5}
fmt.Printf("%T\n", x1)

s1 := x1[:]
fmt.Println(s1, len(s1), cap(s1))
// 指向的内存地址是一样的
fmt.Printf("%p\n", &s1[0])
fmt.Printf("%p\n", &x1[0])
// 删除s1后,
s1 = append(s1[:1], s1[2:]...)
fmt.Println(s1, len(s1), cap(s1))
fmt.Println(x1) // 修改了底层数组[1,5,5] 把2覆盖1了。3没变
fmt.Printf("%p\n", &s1[0])

s1[1] = 100
fmt.Println(x1)  //[1,100,5]

out

[]int
[1 3 5] 3 3  
0xc00000e120 
0xc00000e120 
[1 5] 2 3    
[1 5 5]      
0xc00000e120 
[1 100 5]

可以看到一个很奇怪的现象,当我们删除了s1[1]后,s1正常删除,但是x1却不是,这就是删除操作给底层数组带来的影响,底层数组的[2]还是5没有动,但是[1]元素被删除后,底层数组的[1]被[2]覆盖了。

15. 切片面试题

题目1

写出下面程序运行结果

var s = make([]int, 5, 10)
fmt.Println(s)  // 这一句是调试加的
for i := 0; i < 10; i++ {
	s = append(s, i)
}
fmt.Println(s)
fmt.Println(len(s), cap(s))

out

[0 0 0 0 0]
[0 0 0 0 0 0 1 2 3 4 5 6 7 8 9] 
15 20

这题确实是个坑,以为结果会是这样[0 1 2 3 4 5 6 7 8 9] ,然而却是[0 0 0 0 0 0 1 2 3 4 5 6 7 8 9],是因为一开始初始化的时候就有默认值了。

题目2

写出下面程序运行结果

func main() {
	var s = make([]int, 1)
	s = append(s, 1) 
	fmt.Println(s)
}

out

[0 1]

这题也是个坑!

题目3

func main() {
	var s []int
	s = append(s, 1)
	fmt.Println(s)
}

out

[1]

这题考的是与上面的区别,使用append()可以初始化切片

16. sort.Ints(s[:])排序

import (
	"fmt"
	"sort"
)
func main() {
	var s = []int{3, 1, 7, 11, 8}
	sort.Ints(s[:])  // 是没有返回值的,直接改的就是切片,sort.Ints(s)也是可以的
	fmt.Println(s)
}

out

[1 3 7 8 11]

17. 删除切片元素影响底层数组

func main() {
	var a = [5]int{1, 3, 5, 7, 9}
	s := a[:]
	s = append(s[:1], s[2:]...)
	fmt.Println(s)
	fmt.Println(a)

}

out

[1 5 7 9]
[1 5 7 9 9] 

底层数组的最后一个9没有动,其他元素往前移动了。

标签:s1,cap,基础,len,切片,Println,fmt
From: https://www.cnblogs.com/sunnybowen/p/slice.html

相关文章

  • Bootstrap基础介绍一
    前端框架Bootstrap该框架已经帮你写好了很多页面样式,你如果需要使用,只需要下载它对应文件,之后直接cv拷贝即可在使用Bootstrap的时候所有的页面样式都只需要你通过class来......
  • 51单片机笔记[1]-基础实验
    实验目的掌握使用KEIL,Proteus软件掌握程序下载方法实验内容点亮发光二极管按下K1按键(P2.0),点亮发光二极管(P1.0)LED1按下K2按键(P2.1),LED1~LED8(P1口)双向流水灯P1.0口连......
  • vue3 基础-列表渲染
    本篇讲列表渲染,主要是对v-on指令配合v-if和一些数组相关的方法来体验vue的模板渲染方法.数组元素的渲染<!DOCTYPEhtml><htmllang="en"><head><title>列表......
  • Python3项目初始化10-->JS基础、dom、jquery、database
    29、JS基础var定义变量数字字符串和Python一样布尔值true和false首字母不大写逻辑判断if(){}elseif(){}else{}借助浏览器console执行操作,见截图。   ......
  • jQuery基础介绍二
    jQuery练习题js$('#i1')r.fn.init [div#i1.container]$('h2')r.fn.init [h2,prevObject:r.fn.init(1)]$('input')r.fn.init(9) [input#exampleInputEmail1.f......
  • 2.Linux相关基础操作
    1.用户操作1.添加用户useraddaaa2.指定目录useradd-d/home/aaaaaa3.删除用户userdelaa4.删除用户及目录userdel-raaa5.查看用户信息idaa6.切换用户su......
  • JavaScript基础介绍三(事件)
    原生js事件绑定我们直接写几个案例,看懂即可开关灯案例变色<script>letbtnEle=document.getElementById('d2')letdivEle=document.getElementById('......
  • Python入门系列(三)一学就会-基础数据类型
    数据类型您可以使用type()函数获取任何对象的数据类型。x=5print(type(x))数字类型x=1#inty=2.8#floatz=1j#complexInt,或integer,是一个长度不......
  • Fiddler学习笔记--基础了解(7)
    首次打开Fiddler默认是不会捕获HTTPS,需要配置后方可捕获HTTPStools--FiddlerOptions--HTTPS,然后将界面选项全部勾选上如果我们有远程监控需求,需要切换值connections,......
  • 切片
    数组packagemainimport"fmt"funcprintArray(myArray[4]int){//值拷贝forindex,value:=rangemyArray{fmt.Println("index=",index,",......