Golang基础-Slice基本介绍

Go 语言中的切片(slice)是一种非常重要的动态数组数据结构,它提供了比数组更灵活的功能。切片具有动态大小,可以通过切片操作来动态改变其长度和容量。理解切片的底层实现、扩容机制以及它与数组的关系是掌握 Go 切片操作的关键。

1. 切片(Slice)的基本概念

切片是 Go 中提供的一种引用类型,它是对数组的一层封装,提供了更灵活的数据操作方式。切片由以下三部分组成:

  • 指针:指向底层数组的一个元素的指针。

  • 长度:切片的当前长度,表示切片中元素的个数。

  • 容量:切片的总容量,表示从切片的开始位置到底层数组的末尾位置能够存储的元素数量。

2. 切片的创建

切片可以通过三种方式创建:

  • 使用数组切片

    :通过数组切片得到一个切片。

    arr := [5]int{1, 2, 3, 4, 5}
    s := arr[1:4]  // 创建一个切片,指向 arr[1] 到 arr[3] 的元素
  • 使用 make 函数

    :创建一个指定长度和容量的切片。

    s := make([]int, 3, 5)  // 创建一个长度为 3,容量为 5 的切片
  • 使用字面量

    :直接初始化一个切片。

    s := []int{1, 2, 3}  // 创建一个包含元素的切片

3. 切片的底层实现

切片的底层是基于数组实现的,切片并不存储数据,而是描述了对底层数组的一部分的引用。底层数组通常是在切片创建时自动分配的。Go 语言的切片结构体大致如下:

type sliceHeader struct {
    Data uintptr // 底层数组的指针
    Len  int     // 切片的长度
    Cap  int     // 切片的容量
}

其中:

  • Data:是指向底层数组的指针,表示切片的数据存储位置。

  • Len:是切片的长度,表示当前切片中包含多少个元素。

  • Cap:是切片的容量,表示切片最多可以容纳多少个元素。

4. 切片的扩容机制

切片是动态的,它的大小是可以变化的。切片扩容的机制是 Go 中切片的一个关键特性。

扩容时的行为

Go 在切片长度超过其容量时,会自动进行扩容。当调用 append 函数向切片添加元素,并且超出了当前容量时,Go 会分配一个新的底层数组,并将原数组的数据复制到新数组中。

  • 扩容规则:Go 的扩容机制通常是按照2倍扩容的原则来进行的,即当切片的容量不足时,容量会大约翻倍。

  • 扩容过程:当切片的长度超过容量时,Go 会分配一个更大的底层数组(容量大约是原来的两倍),然后将原切片的元素复制到新的数组中。新的切片会指向这个新的数组,同时其容量会被更新。

append 函数

append 是 Go 中用于向切片添加元素的函数。它的签名如下:

func append(slice []T, elems ...T) []T
  • 如果切片有足够的容量来容纳新元素,append 只会修改切片的长度。

  • 如果切片的容量不足以容纳新元素,append 会创建一个新的切片,并进行扩容。然后返回新切片的引用。

5. 示例:切片的扩容机制

package main
​
import "fmt"
​
func main() {
    s := make([]int, 0, 2)  // 创建一个容量为 2 的切片
    fmt.Println("Initial slice:", s, "Length:", len(s), "Capacity:", cap(s))
​
    s = append(s, 1)  // 添加元素 1
    fmt.Println("After appending 1:", s, "Length:", len(s), "Capacity:", cap(s))
​
    s = append(s, 2)  // 添加元素 2
    fmt.Println("After appending 2:", s, "Length:", len(s), "Capacity:", cap(s))
​
    s = append(s, 3)  // 超过容量,触发扩容
    fmt.Println("After appending 3:", s, "Length:", len(s), "Capacity:", cap(s))
}

输出:

Initial slice: [] Length: 0 Capacity: 2
After appending 1: [1] Length: 1 Capacity: 2
After appending 2: [1 2] Length: 2 Capacity: 2
After appending 3: [1 2 3] Length: 3 Capacity: 4
解释:
  1. 初始时,创建一个长度为 0,容量为 2 的切片。

  2. append 两次将元素 12 加入切片时,容量没有变化,因为当前容量足够。

  3. 当再添加元素 3 时,切片的容量已经不足以容纳新的元素,因此 Go 会自动扩容,将容量增加到原来的两倍(从 2 增加到 4)。

6. 切片的切片操作

Go 中的切片支持从原切片中创建新的切片,通过索引操作来指定切片的起始位置和终止位置:

s := []int{1, 2, 3, 4, 5}
newSlice := s[1:4]  // 创建一个新切片,从 s 的索引 1 到 3(不包括 4)
  • s[low:high]:从 s 中截取从索引 lowhigh-1 之间的元素,生成新的切片。

  • 如果省略 low,表示从切片的开始位置开始。

  • 如果省略 high,表示一直到切片的结尾。

注意: 切片操作并不会创建新的底层数组,而是会引用原数组的一部分。因此,修改新切片的内容会影响原切片的数据。

7. 切片的共享底层数组

多个切片可以共享同一个底层数组。例如:

s1 := []int{1, 2, 3, 4, 5}
s2 := s1[1:4]  // 创建一个新切片,指向原切片的一部分
s2[0] = 100     // 修改 s2 的内容
fmt.Println(s1)  // 输出 [1 100 3 4 5],因为 s1 和 s2 共享同一个底层数组

8. 切片的性能优化

  • 切片的扩容机制是非常高效的,但切片的容量和长度增加时可能会导致内存的重新分配。如果你知道切片的大小,最好预先指定合适的容量(使用 make),以避免频繁扩容。

  • 对于大量的数据,使用切片作为容器是比数组更高效的选择,因为它能够动态地增长而不需要重新分配整个结构。


总结

  • 切片是 Go 中非常灵活和强大的数据结构,底层实现是通过数组来管理的。

  • 切片支持动态扩容,当切片的长度超过当前容量时,会自动进行扩容,通常是以两倍的容量进行增长。

  • 切片的扩容机制保证了在多数情况下的性能,但如果需要优化,可以通过 make 函数预先为切片分配适当的容量。

  • 切片支持灵活的切片操作,并允许多个切片共享同一底层数组,因此修改一个切片的内容会影响所有共享同一底层数组的切片。

通过理解切片的底层实现和扩容机制,可以更有效地利用切片进行性能优化和内存管理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yy_Yyyyy_zz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值