Go语言切片Slice的使用

2022/6/13 23:22:04

本文主要是介绍Go语言切片Slice的使用,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

1、来源于数组的切片

package main

import "fmt"

func main() {
   a := [...]int{1, 2, 3, 4, 5, 6, 7, 8} //添加了...表示数组
   b := a[2:6]                           //切片
   fmt.Println(b)                        //[3 4 5 6] 左包右不包
}

可以省略开始,可以省略结束,可以省略开始和结束

package main

import "fmt"

func main() {
   a := [...]int{1, 2, 3, 4, 5, 6, 7, 8} //数组
   b := a[2:6]                           //切片
   fmt.Println(b)                        //[3 4 5 6] 左包右不包
   c := a[:6]                            //可以省略开始
   fmt.Println(c)                        //[1 2 3 4 5 6]
   d := a[2:]                            //可以省略结束
   fmt.Println(d)                        //[3 4 5 6 7 8]
   e := a[:]                             //可以省略开始和结束
   fmt.Println(e)                        //[1 2 3 4 5 6 7 8]
}

2、切片slice是数组的视图

改变切片的元素,底层数组也会改变

package main

import "fmt"

func update(a []int) {
	a[0] = 100
	a[1] = 1000
}

func main() {
	a := [...]int{1, 2, 3, 4, 5, 6, 7, 8} //数组
	b := a[2:6]                           //切片是数组的视图  查看数组的第2到第6个元素
	fmt.Println(b)                        //[3 4 5 6] 左包右不包

	src := a[:] //底层数组a的全部视图
	fmt.Println(fmt.Sprintf("原始的底层数组全视图是:%d", src))
	update(src)
	fmt.Println(fmt.Sprintf("改变了切片后的底层数组全视图是:%d", src))
	fmt.Println(fmt.Sprintf("原始的底层数组是:%d", a))
}

3、slice可以继续reslice

package main

import "fmt"

func update(a []int) {
   a[0] = 100
   a[1] = 1000
}

func main() {
   a := []int{1, 2, 3, 4, 5, 6, 7, 8} //[]中没有...代表切片
   b := a[2:7]                        //切片可以再次切片,称为reslice
   fmt.Println(b)                     //[3 4 5 6 7]
   c := b[3:]
   fmt.Println(c) //[6 7]
   d := c[:1]
   fmt.Println(d) //[6]
}

4、slice会向底层看齐

如果切片时,切片到头了,则会向底层查看一下是否还有可视的值,因为切片是数组的视图,所以最终会视图到最底层的数组,例如

package main

import "fmt"

func main() {
   arr := [...]int{1, 2, 3, 4, 5, 6, 7, 8}
   fmt.Println(fmt.Sprintf("底层数组:%d", arr))
   s1 := arr[2:6]
   fmt.Println(fmt.Sprintf("arr[2:6]:%d", s1))
   // s1 是 [3 4 5 6] 如果再对s1取3:5,则会出来什么呢
   s2 := s1[3:5]
   fmt.Println(fmt.Sprintf("s1[3:5]:%d", s2))
   //会出来[6 7] 6是来自于切片s2 而7是来自于底层数组arr
}

可以看出s1都没有s1[4]而s2取出来了7,这是由于视图向下看,看到了底层数组的7

说明:

数组arr是1,2,3,4,5,6,7,8

s1 := arr[2:6] 是取数组的第三个元素,总共取4个元素,就是3,4,5,6,但是s1是知道数组arr还有7,8两个元素,因为s1是数组的视图

s2 := s1[3:5]是取s1的第四个元素,总共去2个元素,第一个元素就是6,但是第二个元素在s2中没有了,如果打印s2[4]则会报错,但是因为是视图,s1是知道后面的元素是7,8,因此第二个元素就可以取到7,如果再取一个元素,那就是可以取到8,如果再取一个,因为底层数组到8便没有了,因此抛出异常

5、切片slice的底层数据结构

image-20220612204917905

说明

①slice由ptr和len以及cap组成

ptr是指针,指向切片的第一个元素

len是切片的长度,从ptr开始到结束的元素个数,当下标 超过这个长度后会报下标越界的错误

cap表示切片的容量,表示共可以存多少个元素,当添加元素后,如果容量不够,会自动扩容

⑤slice可以向后扩展,不可以向前扩展,扩展可以超过len的值,但不能超过底层数组的cap

打印切片的len和cap

package main

import "fmt"

func main() {
   arr := [...]int{1, 2, 3, 4, 5, 6, 7, 8}
   fmt.Println(fmt.Sprintf("底层数组:%d", arr))
   s1 := arr[2:6]
   fmt.Println(fmt.Sprintf("arr[2:6]:%d, len:%d, cap:%d", s1, len(s1), cap(s1)))
   // s1 是 [3 4 5 6] 如果再对s1取3:5,则会出来什么呢
   s2 := s1[3:5]
   fmt.Println(fmt.Sprintf("s1[3:5]:%d, len:%d, cap:%d", s2, len(s2), cap(s2)))
   //会出来[6 7] 6是来自于切片s2 而7是来自于底层数组arr
}

image-20220613213005155

6、slice添加元素

使用append添加元素,添加的元素会紧跟在后面,添加后多余cap的数据会被挤出去

append方式作用于底层数组

package main

import "fmt"

func main() {
   arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
   fmt.Println(fmt.Sprintf("底层数组:%d", arr)) //[0 1 2 3 4 5 6 7]
   s1 := arr[2:6]
   fmt.Println(fmt.Sprintf("s1:%d", s1)) //[2 3 4 5]
   s2 := s1[3:4]
   fmt.Println(fmt.Sprintf("s2:%d", s2)) //[5]
   s3 := append(s2, 10)
   fmt.Println(fmt.Sprintf("s3:%d", s3)) //[5 10]
   // 由于s2是[5] append后10直接跟着5后面,把6给直接顶掉了
   //因为slice是底层数组的view,即是底层数组的视图,所以append作用于底层数组
   //底层中的5后面的数6就被10给替换了
   fmt.Println(fmt.Sprintf("底层数组:%d", arr)) //[0 1 2 3 4 5 10 7]

   s4 := append(s3, 11) //[5 10 11]
   fmt.Println(fmt.Sprintf("s4:%d", s4))
   //由于s3是[5 10],直接append一个11,因此11直接跟在10后面,原来底层数组10后面是7,因此把7给替换成了11
   fmt.Println(fmt.Sprintf("底层数组:%d", arr)) //[0 1 2 3 4 5 10 11]

   s5 := append(s4, 12) //[5 10 11 12]
   fmt.Println(fmt.Sprintf("s5:%d", s5))
   //由于s4是[5 10 11]直接append一个12,因此12直接跟在11后面,但是由于再跟一个12,就超过了底层数组的cap,因此会产生一个新的地址存放新的数据
   //如果用debug的方式查看,就能看到产生了新的地址存放新的数组
   fmt.Println(fmt.Sprintf("底层数组:%d", arr))
}

debug方式查看新的地址存放新数据

image-20220613220521434

7、slice的几种创建方式

  • var定义slice
package main

import "fmt"

func main() {
   var s []int//申明切片s,默认是nil
   fmt.Println(fmt.Sprintf("var 定义的 slice默认值:%v", s))
   for i := 0; i < 100; i++ {
      s = append(s, i)//通过append方式添加元素
      fmt.Println(fmt.Sprintf("len:%d cap:%d, value:%d", len(s), cap(s), s))
   }
   //可以看到cap会默认自己扩张

}
  • 冒号:定义slice
package main

import "fmt"

func main() {
   s := []int{2, 4, 6, 8, 10}
   fmt.Println(fmt.Sprintf("len:%d, cap:%d, value:%d", len(s), cap(s), s))
}
  • make关键字创建slice

如果想创建某个长度的slice可以使用make函数

例如:我想创建cap为10的slice,但我只知道其中5个

package main

import "fmt"

func main() {
	s := make([]int, 5, 10)
	s[0] = 10
	s[1] = 1
	s[2] = 10
	s[3] = 1
	s[4] = 10
	fmt.Println(fmt.Sprintf("len:%d, cap:%d, value:%d", len(s), cap(s), s))
}
  • copy关键字创建slice
package main

import "fmt"

func main() {
   s := []int{1, 2, 3, 5}
   fmt.Println(s)
   s1 := []int{0, 0, 0}//[1 2 3 5] [1 2 3]
   copy(s1, s)
   fmt.Println(s, s1)
}

8、删除slice元素

删除中间的元素

package main

import "fmt"

func main() {
   s := []int{1, 2, 3, 4, 5}
   fmt.Println(s)
   s1 := append(s[:2], s[3:]...)//移除3
   fmt.Println(s1)
}

删除首元素

package main

import "fmt"

func main() {
   s := []int{1, 2, 3, 4, 5}
   fmt.Println(s)
   s1 := s[1:]
   fmt.Println(s1)
}

删除尾巴元素

package main

import (
   "fmt"
)

func main() {
   s := []int{1, 2, 3, 4, 5}
   fmt.Println(s)
   s1 := s[:len(s)-1]
   fmt.Println(s1)
}


这篇关于Go语言切片Slice的使用的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程