Go复合数据类型之切片

363 阅读2分钟

「这是我参与11月更文挑战的第13天,活动详情查看:2021最后一次更文挑战

写在前面👀

今天主要讲讲Go的切片🍱

一、什么是切片(Slice)🍘

切片(Slice)是同一种数据类型的长度不固定的序列🍙

  • 虽然切片很像数组,但它并不是数组或数组指针,但他们两者的关系又很密切
  1. 和数组不同的是,切片不需要说明长度
  2. 切片是一个引用类型,是对数组的一个连续片段的引用
  3. 可以简单理解为切片是动态的,数组是静态的,一般来说切片比数组更常用
  4. 切片的构成:指针、长度、容量 切片组成 | 描述 | | ---- | ---------------------------------------- | | 指针 | 指向第一个slice元素对应的底层数组元素的地址 | | 长度 | slice中元素的数目,长度不能超过容量,用len()函数返回切片长度 | | 容量 | 一般是从slice的开始位置到底层数据的结尾位置,用cap()函数返回切片的容量 |

二、切片的创建与初始化🍚

1.直接创建新的切片🍤

  • 语法👇
声明切片:
        var 切片名 []Type   //[]中括号里不需要填,Type表示基本类型
        var a []int     //值为nil
        var b []string  //值为nil
全局初始化:
        var c = []int{} //空切片,值不为nil
        var d = []int{1, 2, 3, 4}
局部初始化:
        e := []string{"a", "b", "c", "d"}
  • 示例👇
package main

import "fmt"

var a []int     //值为nil
var b []string  //值为nil
var c = []int{} //空切片,值不为nil
var d = []int{1, 2, 3, 4}

func main() {
	e := []string{"a", "b", "c", "d"}
	fmt.Println(a, b, c, d, e)
	fmt.Println(a==nil)
	fmt.Println(b==nil)
	fmt.Println(c==nil)
}
  • 结果👇

image.png

2.通过数组或切片创建新切片🍛

切片默认指向一段连续内存区域,可以是数组,也可以是切片本身🍜

  • 语法👇 s表示的是数组或切片🦪 |操作 | 结果 | | ------------ | ------------------------------------------- | | s[start:end] | 截取的范围是索引start<=index<end,长度为end-start | | s[start:] | 截取的范围是索引start<=index<len(s),长度为len(s)-statt | | s[:end] | 截取的范围是索引0<=index<end,长度为end | | s[:] | 截取的范围是s本身,长度为len(s)| |s[0:0]|等效于空切片,一般用于切片复位|

  • 示例👇

package main

import "fmt"

var s1 = [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} //创建一个int型数组
/*通过数组创建切片*/
var s2 []int = s1[1:5]                          //全局变量
var s3 = s1[1:]                                 //也可以不写[]int

func main() {
	/*局部变量*/
	s4 := s1[:9]
	s5 := s1[:]
	s6 := s1[0:0]
	/*输出*/
	fmt.Printf("s1 type:%T value:%v len:%d cap:%d\n", s1, s1, len(s1), cap(s1))
	fmt.Printf("s2 type:%T value:%v len:%d cap:%d\n", s2, s2, len(s2), cap(s2))
	fmt.Printf("s3 type:%T value:%v len:%d cap:%d\n", s3, s3, len(s3), cap(s3))
	fmt.Printf("s4 type:%T value:%v len:%d cap:%d\n", s4, s4, len(s4), cap(s4))
	fmt.Printf("s5 type:%T value:%v len:%d cap:%d\n", s5, s5, len(s5), cap(s5))
	fmt.Printf("s6 type:%T value:%v len:%d cap:%d\n", s6, s6, len(s6), cap(s6))

}

  • 结果👇

image.png

  • 注意点
  1. 可以发现s1和s5输出的格式、长度、容量都一样,但他们的类型是不同的!s1是数组,s5是切片
  2. s3和s4明明长度相同,元素个数也一样,但他们的容量不相同。这是因为容量是从start位置(即冒号:左边)开始算起的
  3. s6是空切片,长度为零,但系统已经为它分配了内存

3.使用make()函数初始化切片🍣

  • 语法👇
make([]Type,len,cap) //Type表示类型,len表示要生成的长度,cap(为可选参数)表示容量
  • 示例👇 make()函数的作用是初始化切片(初始化切片的长度和容量),初始化以后就有了内存空间
/*错误做法❗❗❗*/
package main

import "fmt"

var a []int //声明一个切片,但未初始化,是nil切片
func main() {
	a[0] = 1 //nil切片没有内存,不能直接赋值
	fmt.Println(a)
}
/*正确做法✅*/
package main

import "fmt"

var a []int //声明一个切片,但未初始化,是nil切片
func main() {
	a = make([]int, 5,10) //使用make()初始化切片的长度和容量
	a[0] = 1              //分配了内存后不为nil,可以直接赋值,且未赋值的为默认值0
	fmt.Println(a)
}


  • 结果👇
  1. 错误结果❌ 编译器会报错! image.png
  2. 正确结果✔

image.png

三、用for range遍历切片🍥

  • 示例👇
package main

import (
	"fmt"
)

func main() {

	s := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

	for index, value := range s {
		fmt.Printf("inde : %v , value : %v\n", index, value)
	}

}

  • 结果👇

image.png

四、用append()为切片追加元素🥮

1.基本用法🍢

为S1增加元素:s1:=append(S1,element)

  • 示例👇
package main

import "fmt"

var a []int
var b = []int{5, 6, 7}

func main() {
	a = append(a, 1)       // 为a追加1个元素
	a = append(a, 2, 3, 4) // 为a追加多个元素
	a = append(a, b...)    // 为a追加一个切片, 切片需要用...解包
	fmt.Println(a)
}

  • 结果👇

image.png

2.注意点🥘

  1. 切片容量充足,在原切片基础上追加,无内存 op。
  2. 切片容量不足,会分配新的切片空间,有内存 op。性能相对低。
  3. 扩容规律按切片容量的 2 倍进行扩容
  • 示例👇
package main

import "fmt"

var a []int

func main() {
	for i := 0; i < 10; i++ {
		a = append(a, i)
		fmt.Printf("len:%d cap:%d  pointer:%p  value:%v\n", len(a), cap(a), a, a)
	}
}

  • 结果👇

image.png

3. 向指定位置插入元素🧆

  1. 用for range和append做🍲
  • 示例👇
package main

import "fmt"

/*posion表示插入的位置,source表示目标切片,target表示待插入切片*/
func SliceInsert(source []int, posion int, target []int) []int {
	result := []int{}
	for k, v := range source {
		result = append(result, v)
		if k == posion-1 {
			result = append(result, target...)
		}
	}
	return result
}

func main() {
	source := []int{1, 4, 5, 6}
	target := []int{2, 3}
	fmt.Println(SliceInsert(source, 1, target))
}

  • 结果👇

image.png 2. 用两个append做🍝

  • 示例👇
package main

import "fmt"

/*posion表示插入的位置,source表示目标切片,target表示待插入切片*/
func SliceInsert(source []int, posion int, target []int) []int {
	source = append(source[:posion], append(target, source[posion:]...)...)
	return source
}

func main() {
	source := []int{1, 4, 5, 6}
	target := []int{2, 3}
	fmt.Println(SliceInsert(source, 1, target))
}

  • 结果👇

image.png

4. 删除索引为i的元素🥣

s2:=append(s1[:i], s1[i+1:]...)

5.删除索引i-j的元素🥧

s2 := append(append([]int{}, s1[:i]...), s1[j:]...)

五、用copy()复制一个切片到另一个切片🍦

copy(s2,s1) //把s1的内容复制到s2中去,要注意s2的长度要足够接受s1,才能完整复制
  • 示例👇
package main

import "fmt"

func main() {
	s1 := []int{1, 2, 3}
	s2 := make([]int, 1)
	s3 := make([]int, 3)
	copy(s2, s1) //s2长度不够,不能完整复制
	copy(s3, s1) //s3长度够,能完整复制
	fmt.Println("s1:", s1)
	fmt.Println("s2:", s2)
	fmt.Println("s3:", s3)
}

  • 结果👇

image.png

六、其他🍧

  • 切片能和其他类型结合起来用,如mapslice、slicemap、结构体切片等等,我会在后面的文章提及到🍨

写在后面🙌

感谢观看✨
如有不足,欢迎指出💖