Go 语言的切片使用

Posted 小伍

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go 语言的切片使用相关的知识,希望对你有一定的参考价值。

切片(slice)是建立在数组之上的更方便,更灵活,更强大的数据结构。切片并不存储任何元素而只是对现有数组的引用。

切片的三要素

  • 指向数组中的开始位置
  • 切片的长度,通过内置函数len获得
  • 切片的最大容量,通过内置函数cap获得

可以认为切片在内部表示为如下的结构体:

type slice struct {
    // 长度
    Length        int
    // 容量
    Capacity      int
    // 指向首元素的指针
    ZerothElement *byte
}

切片声明

// 基于数组创建一个从 a[start] 到 a[end -1] 的切片
a := [5]int{76, 77, 78, 79, 80}
var b []int = a[1:4]

// 创建了一个长度为4的string数组,并返回一个切片给names
names := []string{"beijing", "shanghai", "guangzhou", "shenzhen"}

// 创建长度为10的字符串切片
str := make([]string, 10)

// 创建长度为10,容量为20的字符串切片
str := make([]string, 10, 20)

arr[start:end] 的使用

// 基于数组产生新的切片,a[start:end]为左闭右开
name1 := names[0:3]
name2 := names[:3]
name3 := names[2:]
name4 := names[:]

引用传递

切片本身不包含任何数据。它仅仅是底层数组的一个上层表示。对切片进行的任何修改都将反映在底层数组中。

names := []string{"beijing", "shanghai", "guangzhou", "shenzhen"}
name1 := names[0:3]

// 引用传递,会同时改变原数组
name1[2] = "luoyang"

fmt.Println(names)
fmt.Println(name1)

获取切片长度与容量

str := "helloworld"[5:7]
fmt.Println("len:", len(str), ", cap:", cap(str))

切片追加元素

// append会判断切片是否有剩余空间,如果没有剩余空间,则会自动扩充两倍空间
names = append(names, "chongqing")
如果切片是建立在数组之上的,而数组本身不能改变长度,那么切片是如何动态改变长度的呢?实际发生的情况是,当新元素通过调用 append 函数追加到切片末尾时,如果超出了容量,append 内部会创建一个新的数组。并将原有数组的元素被拷贝给这个新的数组,最后返回建立在这个新数组上的切片。这个新切片的容量是旧切片的二倍。

切片追加切片

可以使用 ... 操作符将一个切片追加到另一个切片末尾

veggies := []string{"potatoes","tomatoes","brinjal"}
fruits := []string{"oranges","apples"}
food := append(veggies, fruits...)
fmt.Println("food:", food)

遍历切片

for key, value := range names {
    fmt.Println("key:", key, ", value:", value)
}

复制切片

copy用于将内容从一个数组切片复制到另一个数组切片。

slice1 := []int{1, 2, 3, 4, 5} 
slice2 := []int{5, 4, 3} 

copy(slice2, slice1) // 只会复制slice1的前3个元素到slice2中 
copy(slice1, slice2) // 只会复制slice2的3个元素到slice1的前3个位置

切片作为函数参数

func subtactOne(numbers []int) {  
    for i := range numbers {
        numbers[i] -= 2
    }
}

func main() {
    nos := []int{8, 7, 6}
    fmt.Println("slice before function call", nos)
    subtactOne(nos)                               //function modifies the slice
    fmt.Println("slice after function call", nos) //modifications are visible outside
}

// array before function call [8 7 6]  
// array after function call [6 5 4]  

切片内存优化

切片保留对底层数组的引用。只要切片存在于内存中,数组就不能被垃圾回收。假设我们有一个非常大的数组,而我们只需要处理它的一小部分,为此我们创建这个数组的一个切片,并处理这个切片。这时候数组仍然存在于内存中,因为切片正在引用它。

解决该问题的一个方法是使用 copy 函数来创建该切片的一个拷贝。这样我们就可以使用这个新的切片,原来的数组可以被垃圾回收。

func countries() []string {  
    countries := []string{"USA", "Singapore", "Germany", "India", "Australia"}
    // 数组被切片引用,不能回收
    neededCountries := countries[:len(countries)-2]
    // 创建新的切片
    countriesCpy := make([]string, len(neededCountries))
    // 使用 copy 函数创建切片的拷贝
    copy(countriesCpy, neededCountries) //copies neededCountries to countriesCpy
    // 返回后数组不再被引用,自动回收
    return countriesCpy
}

func main() {  
    countriesNeeded := countries()
    fmt.Println(countriesNeeded)
}

以上是关于Go 语言的切片使用的主要内容,如果未能解决你的问题,请参考以下文章

Go语言数据结构切片

go——切片

Go语言 使用append() 为切片动态添加元素

Go语言 排序与搜索切片

7Go语言基础之切片(slice)

Go 语言泛型编程之切片