Go从入门到精通——切片(slice)(动态分配大小的连续空间)

切片(slice)(动态分配大小的连续空间)

  Go 语言切片的内部结构包含地址、大小和容量。切片一般用于快速地操作一块数据集合。

1.1、从数组或切片生成新的切片

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

  从连续内存区域生成切片是常见的操作。格式如下:

slice [开始位置:结束位置]
//slice 表示目标切片对象。
//开始位置对应目标切片对象的索引。
//结束位置对应目标切片的结束索引。

  从数组生成切片例子:

package main

import (
	"fmt"
)

func main() {
	var a = [3]int{1, 2, 3} //一个变量a的数组,数组大小是3,已初始化数值1,2,3.
	fmt.Println(a, a[1:2])  //使用 a[1:2]生成一个新切片
}

  从数组或切片生成新的切片拥有如下特性:a :=[]int[1,2,3,4,5,6,7,8,9,10]

  • 取出的元素数量为:结束位置 - 开始位置
  • 取出元素不包含结束位置对应的索引,切片最后一个元素使用 slice[len(slice)] 获取。
  • 当缺省开始位置时,表示从连续区域开头到结束位置,比如 a[:10]。
  • 当缺省结束位置时,表示从开始位置到整个连续区域末尾,比如 a[1:]。
  • 两者时同时缺省时,与切片本身等效,比如:a[:]。
  • 两者同时为 0 时,等效于空切片,一般用于切片复位,比如 a[0:0]
  • 根据索引位置取切片 slice 元素值时,取值范围是(0~len(slice)-1),超界会报运行时错误。生成切片时,结束位置可以填写 len(slice) 但不会报错。

1.2、声明切片

  每一种类型都可以拥有其切片类型,表示多个类型元素的连续集合。因此切片类型也可以被声明。

  切片类型声明格式如下:

var name []T

//name 表示切片类型的变量名
//T 表示切片类型声明的使用过程

1.3、使用 make() 函数构造切片

  如果需要动态地创建一个切片,可以使用 make() 内建函数,格式如下:

make( []T, size, cap)
// T: 切片的元素类型。
// size: 就是为这个类型分配多少个月元素。
// cap: 预分配的元素数量,这个值设定后不影响 size,只是能提前分配空间,降低多次分配空间造成的性能问题。

 注意:

  • 使用 make() 函数生成的切片一定是发生了内存分配操作。
  • 给定开始与结束位置(包括切片复位)的切片只是将新的切片结构指向已经分配好的内存区域,设定开始与结束位置,不会发生内存分配操作。
  • 切片不一定就必须经过 make() 函数才能使用。
  • 生成的切片,声明后使用 append() 函数均可以正常使用切片。

1.4、使用 append() 函数为切片添加元素

  Go 语言的内建函数 append() 可以为切片动态添加元素。每个切片会指向一个内存空间,这片空间能容纳一定数量的元素。当空间不能再容纳足够多的元素时,切片就会进行 "扩容"。"扩容" 操作往往发生在 append() 函数调用时。

  切片在扩容时,容量的扩展规律按容量的 2 倍数扩充,例如 1、2、4、8、16……,代码如下:

package main

import "fmt"

func main() {
	var numbers []int

	for i := 0; i < 10; i++ {
		numbers = append(numbers, i)
		fmt.Printf("len: %d\t cap:%d pointer: %p\n", len(numbers), cap(numbers), numbers)
	}

}

   append() 函数除了添加一个元素外,也可以一次性添加很多元素。

package main

import "fmt"

func main() {
	var car []string

	// 添加 1 个元素
	car = append(car, "热烈欢迎世界各地") //使用 append() 函数向切片中添加 1 个元素
	fmt.Println("添加 1 个元素:", car)

	// 添加 2 个元素
	car = append(car, "参加", "2022北京冬奥会", "的代表团!") //使用 append() 函数向切片中添加 多 个元素
	fmt.Println("添加 2 个元素:", car)

	// 添加切片
	team := []string{"Warmly welcome the delegations from all over the world to the 2022 Beijing Winter Olympic Games!"}
	car = append(car, team...) //在 team 后面加上了 "...",表示将 team 整个添加到 car 的后面。
	fmt.Println(car)

}

1.5、复制切片元素到另一个切片

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

copy(destSlice, srcSlice []T) int
//srcSlice 为数据来源切片;
//destSlice 为复制目标(来源的切片复制给谁)。目标切片必须分配过空间且足够承载复制的元素个数。来源和目标的类型一致,copy 的返回值表示实际发生复制的元素个数。

  下面的代码将演示对切片的引用和复制操作后对切片元素的影响:

package main

import "fmt"

func main() {

	//设置元素数量为 1000
	const elementCount = 1000

	//预分配足够多的元素切片
	srcData := make([]int, elementCount)

	//将切片赋值
	for i := 0; i < elementCount; i++ {
		srcData[i] = i //srcData 切片中存储 1000 个元素,元素值分别是连续的 1-1000 数字
	}

	fmt.Printf("srData 切片的值: %d\n", srcData)

	//引用切片数据
	//定义一个变量,并将 srcData 值传递给它。切片赋值,实际上只是复制了地址,refData 引用了 srcData的地址,因此 srcData的任何修改,都会影响 refData
	refData := srcData
	fmt.Printf("refData 的内存地址: %p\nsrcData 的内存地址: %p\n", &refData, &srcData)

	//预分配足够多的元素切片
	copyData := make([]int, elementCount)
	fmt.Printf("copyData 的内存地址: %p\n", &copyData)

	//将数据复制到新的切片空间中
	copy(copyData, srcData)
	fmt.Printf("复制 srcData 后的 copyData 的内存地址: %p\n", &copyData)

	//修改原始数据的第一个元素
	srcData[0] = 999
	fmt.Printf("修改 srcData 第 1 个元素后的 copyData 的内存地址: %p\n", &copyData)

	//打印引用切片的一个元素
	fmt.Printf("打印引用切片( refData )的一个元素: %d 内存地址: %p\n", refData[0], &refData)

	//打印复制切片的第一个和最后一个元素
	fmt.Printf("打印复制切片( copyData )的一个元素: %d 内存地址: %p\n", copyData[0], &copyData)

	//复制原始数据从 4 到 6(不包含)
	for i := 0; i < 5; i++ {
		fmt.Printf("%d", copyData[i])
	}
	fmt.Println(srcData[4:6])
	
	fmt.Println(srcData)

	copy(copyData, srcData[4:6])
	for i := 0; i < 5; i++ {
		fmt.Printf("%d", copyData[i])
	}
}

  代码输出如下:

 

1.6、从切片中删除元素

  Go 语言中并没有对删除切片元素提供专用的语法或者接口,需要使用切片本身的特性来删除元素。

package main

import "fmt"

func main() {

	seq := []string{"a", "b", "c", "d", "e"}

	//指定删除位置
	index := 2

	//查看删除位置之间的元素和之后的元素
	fmt.Println(seq[:index], seq[index+1:])

	// 将删除点前后的元素连接起来
	seq = append(seq[:index], seq[index+1:]...) //后面这3个点要加上,不然会报错,用于两个切片合并的。

	fmt.Println(seq)
}

posted @ 2022-02-14 14:13  左扬  阅读(1083)  评论(0编辑  收藏  举报
levels of contents