切片


十一、切片

Go 語言切片是對數組的抽象,因此切片是引用類型。但自身是結構體,值拷貝傳遞。

Go 數組的長度不可改變,在特定場景中這樣的集合就不太適用,Go 中提供了一種靈活,功能強悍的內置類型切片("動態數組"),與數組

相比切片的長度是不固定的,可以追加元素,在追加時可能使切片的容量增大。

切片本身不擁有任何數據,它是對現有數組的引用。

切片遍歷方式和數組一樣,可以用len()求長度。表示可用元素數量,讀寫操作不能超過該限制。

1 切片的定義

你可以聲明一個未指定大小的數組來定義切片,切片不需要說明長度。

var 變量名 []類型

// 比如 var str []string 
//     var arr []int。
// []括號內什么都不寫 就是切片類型

或使用 make() 函數來創建切片:

var slice []type = make([]type, len)

// 也可以簡寫,其中 capacity 為可選參數。這里 len 是數組的長度並且也是切片的初始長度

slice := make([]type, len)
slice := make([]type, length, capacity)

2 創建切片的各種方式

func main() {
	//1.聲明切片
	var s1 []int
	if s1 == nil {
		fmt.Println("是空")
	} else {
		fmt.Println("不是空")
	}
	// 2.:=
	s2 := []int{}
	// 3.make()
	var s3 []int = make([]int, 0)
	fmt.Println(s1, s2, s3)
	// 4.初始化賦值
	var s4 []int = make([]int, 0, 0)
	fmt.Println(s4)
	s5 := []int{1, 2, 3}
	fmt.Println(s5)
	// 5.從數組切片
	arr := [5]int{1, 2, 3, 4, 5} // 定義數組
	var s6 []int  // 定義切片
	// 索引區間前閉后開
	s6 = arr[1:4]
	fmt.Println(s6)
}

一個切片在未初始化之前默認為 nil,長度為 0。

3 切片初始化

s :=[] int {1,2,3 } 

直接初始化切片,[] 表示是切片類型,{1,2,3} 初始化值依次是 1,2,3,其 cap=len=3

var arr = [...]int{0,1,2,3,4,5,6,7,8,9,}
var slice0 []int = arr[start:end]
var slice1 []int = arr[:end]
var slice2 []int = arr[start:]
var slice3 []int = arr[:]
var slice4 = arr[:len(arr)-1]

舉例:

var arr = [...]int{0,1,2,3,4,5,6,7,8,9,}
var slice0 []int = arr[2:8]			// 左閉右開,len=high-low
var slice1 []int = arr[0:6]        //0可以省略: var slice []int = arr[:end]
var slice2 []int = arr[5:10]       //如果切片到結尾,可以省略: var slice[]int = arr[start:]
var slice3 []int = arr[0:len(arr)] //var slice []int = arr[:]
var slice4 = arr[:len(arr)-1]      //去掉切片的最后一個元素
fmt.Printf("arr %v\n", arr)
fmt.Printf("slice0 %v\n", slice0)
fmt.Printf("slice1 %v\n", slice1)
fmt.Printf("slice2 %v\n", slice2)
fmt.Printf("slice3 %v\n", slice3)
fmt.Printf("slice4 %v\n", slice4)

// 輸出:
arr [0 1 2 3 4 5 6 7 8 9]
slice0 [2 3 4 5 6 7]
slice1 [0 1 2 3 4 5]
slice2 [5 6 7 8 9]
slice3 [0 1 2 3 4 5 6 7 8 9]
slice4 [0 1 2 3 4 5 6 7 8]

4 len() 和 cap() 函數

切片是可索引的,並且可以由 len() 方法獲取長度。

切片提供了計算容量的方法 cap() 可以測量切片最長可以達到多少。

以下為具體實例:

package main

import "fmt"

func main() {
   var numbers = make([]int,3,5)

   printSlice(numbers)
}

func printSlice(x []int){
   fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}

// 輸出
len=3 cap=5 slice=[0 0 0]

5 追加切片元素

正如我們已經知道數組的長度是固定的,它的長度不能增加。 切片是動態的,使用 append 可以將新元素追加到切片上。append 函數的定義是

func append(s[]T,x ... T)[]T
// x … T 在函數定義中表示該函數接受參數 x 的個數是可變的。這些類型的函數被稱為[可變函數]。
// append :向 slice 尾部添加數據,返回新的 slice 對象。

如果切片由數組支持,並且數組本身的長度是固定的,那么切片如何具有動態長度。以及內部發生了什么?

當新的元素被添加到切片時,會創建一個新的數組。現有數組的元素被復制到這個新數組中,並返回這個新數組的新切片引用。

新切片的容量是舊切片的兩倍。

舉例:

cars := []string{"Ferrari", "Honda", "Ford"}
	fmt.Println("cars:", cars, "has old length", len(cars), "and capacity", cap(cars)) // capacity of cars is 3
	cars = append(cars, "Toyota")
	fmt.Println("cars:", cars, "has new length", len(cars), "and capacity", cap(cars)) // capacity of cars is doubled to 6

// 輸出
cars: [Ferrari Honda Ford] has old length 3 and capacity 3
cars: [Ferrari Honda Ford Toyota] has new length 4 and capacity 6

6 切片的長度和容量

切片的長度是切片中的元素數。

切片的容量是從創建切片索引位置開始的底層數組中元素數。

7 copy() 函數和內存優化

切片持有對底層數組的引用。只要切片在內存中,數組就不能被垃圾回收。在內存管理方面,這是需要注意的。讓我們假設我們有一個非

常大的數組,我們只想處理它的一小部分。然后,我們由這個數組創建一個切片,並開始處理切片。這里需要重點注意的是,在切片引用

時數組仍然存在內存中。

一種解決方法是使用 [copy] 函數 func copy(dst,src[]T)int 來生成一個切片的副本。這樣我們可以使用新的切片,原始數組可以被

垃圾回收。

func countries() []string {
    countries := []string{"USA", "Singapore", "Germany", "India", "Australia"}
    neededCountries := countries[:len(countries)-2] // 創建一個去掉尾部 2 個元素的切片
    countriesCpy := make([]string, len(neededCountries))
    copy(countriesCpy, neededCountries) //copies neededCountries to countriesCpy
    return countriesCpy
}
func main() {
    countriesNeeded := countries()
    fmt.Println(countriesNeeded)
}

//現在 countries 數組可以被垃圾回收, 因為 neededCountries 不再被引用。

8 切片的函數傳遞

切片在內部可由一個結構體類型表示。這是它的表現形式,

type slice struct {  
    Length        int
    Capacity      int
    ZerothElement *byte
}

切片包含長度、容量和指向數組第零個元素的指針。當切片傳遞給函數時,即使它通過值傳遞,指針變量也將引用相同的底層數組。因

此,當切片作為參數傳遞給函數時,函數內所做的更改也會在函數外可見。


免責聲明!

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



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