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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM