一、Go基础知识11、切片详解

一、切片的定义

在Go语言中,切片是一个引用类型,用于封装了一个动态数组的视图。切片提供了一种方便、灵活的方式来操作数组。

切片的定义

在Go语言中,切片的定义使用以下语法:

var sliceName []Type

其中,sliceName 是切片的名称,Type 是切片存储的元素类型。

示例

package main

import "fmt"

func main() {
    // 定义一个切片
    var numbers []int
    fmt.Println(numbers) // 输出: []
}

二、切片的初始化方式

可以通过切片字面量、make函数、或者从已有数组/切片中切出子切片的方式进行切片的赋值。

1、使用切片字面量

package main

import "fmt"

func main() {
    // 使用切片字面量创建切片
    numbers := []int{1, 2, 3, 4, 5}
    fmt.Println(numbers) // 输出: [1 2 3 4 5]
}

2、使用make函数

package main

import "fmt"

func main() {
    // 使用make函数创建切片,参数为类型、长度、容量
    numbers := make([]int, 5, 10)
    fmt.Println(numbers) // 输出: [0 0 0 0 0]
}

3、从已有数组/切片中切出子切片

package main

import "fmt"

func main() {
    // 从已有数组中切出子切片
    array := [5]int{1, 2, 3, 4, 5}
    slice := array[1:4]
    fmt.Println(slice) // 输出: [2 3 4]
}

解释
这里,array[1:4] 表示从数组的索引1开始(包含),到索引4结束(不包含)的切片。

三、切片的数据访问

在Go语言中,切片的数据访问可以通过索引来实现。切片是一个动态数组的视图,因此可以使用索引来访问切片中的元素。

示例

package main

import "fmt"

func main() {
    // 创建一个切片
    numbers := []int{1, 2, 3, 4, 5}

    // 使用索引访问切片中的元素
    fmt.Println("Element at index 2:", numbers[2]) // 输出: Element at index 2: 3

    // 修改切片中的元素
    numbers[2] = 10
    fmt.Println("Modified slice:", numbers) // 输出: Modified slice: [1 2 10 4 5]

    // 切片的长度和容量
    fmt.Println("Length:", len(numbers))   // 输出: Length: 5
    fmt.Println("Capacity:", cap(numbers)) // 输出: Capacity: 5

    // 遍历切片中的元素
    fmt.Println("Elements:")
    for i, value := range numbers {
        fmt.Printf("Index: %d, Value: %d\n", i, value)
    }
    /*
    输出:
    Elements:
    Index: 0, Value: 1
    Index: 1, Value: 2
    Index: 2, Value: 10
    Index: 3, Value: 4
    Index: 4, Value: 5
    */

    // 切片的切割
    slice1 := numbers[1:4]
    fmt.Println("Slice1:", slice1) // 输出: Slice1: [2 10 4]

    // 切片的追加元素
    numbers = append(numbers, 6, 7, 8)
    fmt.Println("After append:", numbers) // 输出: After append: [1 2 10 4 5 6 7 8]
}

解释
上述例子中,首先创建了一个切片 numbers,然后通过索引访问切片中的元素,修改了切片中的某个元素的值。接着,使用 len 函数获取切片的长度,使用 cap 函数获取切片的容量。通过 for 循环遍历切片中的元素,最后演示了切片的切割和追加元素的操作。

注意
切片是引用类型,对切片的修改会影响原始切片及其底层数组。

四、通过省略号添加多个元素到切片

在Go语言中,可以使用省略号(...)语法将一个切片的元素追加到另一个切片中。这种方式可以方便地将一个切片的所有元素追加到另一个切片中,使代码更简洁。

示例

package main

import "fmt"

func main() {
    // 创建两个切片
    slice1 := []int{1, 2, 3}
    slice2 := []int{4, 5, 6}

    // 使用省略号将 slice2 的所有元素追加到 slice1 中
    slice1 = append(slice1, slice2...)
    fmt.Println("Combined slice:", slice1) // 输出: Combined slice: [1 2 3 4 5 6]

    // 创建一个切片
    numbers := []int{7, 8, 9}

    // 使用省略号将多个元素追加到切片中
    slice1 = append(slice1, 7, 8, 9)
    fmt.Println("Updated slice:", slice1) // 输出: Updated slice: [1 2 3 4 5 6 7 8 9]
}

解释
在上面的例子中,首先创建了两个切片 slice1slice2,然后使用 append 函数和省略号语法将 slice2 的所有元素追加到 slice1 中,形成一个新的切片 Combined slice。接着,演示了在切片末尾使用省略号直接追加多个元素到切片中的操作,形成一个更新后的切片 Updated slice

作用
这种省略号语法的使用使得切片的追加操作更加灵活和简洁。

五、切片的元素删除与拷贝

在Go语言中,切片的元素删除可以通过切片的操作和append函数实现。另外,切片的元素拷贝可以使用copy函数。以下是详细的例子和结果:

1、元素删除

package main

import "fmt"

func main() {
    // 创建一个切片
    numbers := []int{1, 2, 3, 4, 5}

    // 删除切片中的第三个元素(索引为2)
    indexToRemove := 2
    numbers = append(numbers[:indexToRemove], numbers[indexToRemove+1:]...)
    fmt.Println("After deletion:", numbers) // 输出: After deletion: [1 2 4 5]

    // 或者使用切片操作
    numbers = append(numbers[:2], numbers[3:]...)
    fmt.Println("After deletion using slicing:", numbers) // 输出: After deletion using slicing: [1 2 5]
}

解释
在上述例子中,通过append函数和切片操作实现了切片中元素的删除。第一个例子中,通过切片操作删除了索引为2的元素,第二个例子中使用了append函数和切片操作一起删除了索引为2的元素。

2、元素拷贝

package main

import "fmt"

func main() {
    // 创建两个切片
    source := []int{1, 2, 3, 4, 5}
    destination := make([]int, len(source))

    // 使用copy函数将source切片的元素拷贝到destination切片中
    copy(destination, source)
    fmt.Println("Copied slice:", destination) // 输出: Copied slice: [1 2 3 4 5]

    // 修改source切片,不影响destination切片
    source[0] = 10
    fmt.Println("Source after modification:", source)         // 输出: Source after modification: [10 2 3 4 5]
    fmt.Println("Destination after source modification:", destination) // 输出: Destination after source modification: [1 2 3 4 5]
}

解释
在这个例子中,使用copy函数将source切片的元素拷贝到destination切片中,这样就创建了一个新的切片,对原切片的修改不会影响到拷贝后的切片。

注意
copy函数的目标切片必须有足够的容量来容纳源切片的元素。如果目标切片的长度小于源切片的长度,只会拷贝目标切片长度的元素。

3、元素删除与拷贝的总结与区别

切片的元素删除和拷贝是两个不同的操作,它们在实现和目的上有一些明显的区别。

3.1、切片元素删除

在Go语言中,切片的元素删除通常通过以下两种方式实现:

  1. 使用append和切片操作:

    numbers = append(numbers[:indexToRemove], numbers[indexToRemove+1:]...)
    

    这种方式利用append函数和切片操作,将要删除的元素前半部分和后半部分重新拼接起来,从而实现元素的删除。这样的操作会创建一个新的切片,原始切片的底层数组不受影响。

  2. 使用切片操作:

    numbers = append(numbers[:2], numbers[3:]...)
    

    这种方式直接使用切片操作,将要删除的元素前半部分和后半部分拼接起来,也能实现元素的删除。与第一种方式相比,这种方式更简洁,但原理相同。

3.2、切片元素拷贝

切片的元素拷贝主要通过copy函数来实现:

copy(destination, source)

这里,source是要拷贝的源切片,destination是目标切片。copy函数会将source切片的元素拷贝到destination切片中。需要注意的是,destination切片的长度必须足够大,以容纳source切片的所有元素。

3.3、区别总结

  • 元素删除 主要通过append函数和切片操作实现,产生一个新的切片。
  • 元素拷贝 主要通过copy函数实现,创建一个新的切片,原切片的修改不会影响到拷贝后的切片。

总体而言,切片的删除操作涉及切片的重新构建,而切片的拷贝操作则涉及将一个切片的元素复制到另一个切片。

六、切片的基本原理

Go语言中的切片(Slice)是一种动态数组的抽象。切片本质上是对底层数组的封装,提供了一种方便、灵活且高效的方式来操作数组的片段。

1、切片的结构

切片的结构包含三个字段:

  1. 指向底层数组的指针(Pointer):

    • 指示切片开始的位置。
  2. 切片的长度(Length):

    • 切片包含的元素个数。
  3. 切片的容量(Capacity):

    • 切片可以容纳的元素个数,从切片的开始位置到底层数组的末尾。

切片是一个动态数组的引用,其结构如下:

type slice struct {
    ptr      *ElementType // 指向底层数组的指针
    len      int           // 切片的长度
    cap      int           // 切片的容量
}
  • ptr: 指向底层数组的指针,指示切片的起始位置。
  • len: 切片的长度,表示切片包含的元素个数。
  • cap: 切片的容量,表示切片可以容纳的最大元素个数。

2、切片的创建

切片可以通过以下方式创建:

// 通过切片字面量创建切片
slice1 := []int{1, 2, 3, 4, 5}

// 通过make函数创建切片,make([]T, length, capacity)
slice2 := make([]int, 3, 5)

3、切片的底层数组

切片不拥有数据,它只是底层数组的一个视图。当我们创建切片时,Go语言会在底层数组上创建一个新的切片结构,包含指向底层数组的指针、切片的长度和切片的容量。多个切片可以共享同一个底层数组。

array := [...]int{1, 2, 3, 4, 5}
slice1 := array[1:4] // 切片1:[2 3 4]
slice2 := array[0:2] // 切片2:[1 2]

在上述例子中,slice1slice2 共享同一个底层数组 array

4、切片的扩容

当切片的容量不足以容纳新的元素时,切片就会被扩容。Go语言的切片扩容策略是以2的幂次方进行扩容,确保切片的容量始终是2的幂次方。

slice := make([]int, 3, 5)
newSlice := append(slice, 4, 5)

在上述例子中,如果 slice 的容量不足以容纳两个新的元素,就会创建一个新的底层数组,将原有元素拷贝到新数组中,并返回新数组的切片。这确保了在大多数情况下,切片的操作都是高效的。

5、切片的传递

切片是引用类型,当切片作为参数传递给函数时,函数接收到的是切片的拷贝,但拷贝的是切片结构,不包括底层数组。因此,在函数内部对切片的修改会影响到原始切片。

func modifySlice(s []int) {
    s[0] = 99
}

originalSlice := []int{1, 2, 3}
modifySlice(originalSlice)
fmt.Println(originalSlice) // 输出: [99 2 3]

6、切片的引用与共享

由于切片只是对底层数组的引用,所以多个切片可以引用同一个底层数组的不同部分。修改一个切片的元素会影响到其他引用同一底层数组的切片。

slice1[0] = 99
fmt.Println(array) // 输出: [1 99 3 4 5]

在上述例子中,修改 slice1 的第一个元素也修改了底层数组 array,因此 array 的第二个元素变为了 99。

7、切片的零值

切片的零值是 nil。一个值为 nil 的切片没有底层数组,长度为0,容量为0。

var nilSlice []int
fmt.Println(nilSlice == nil) // 输出: true

七、切片底层存储原理通俗易懂版

切片就像是一把智能剪刀,有三个要害部分——指针、长度和容量。指针指向一串连续的元素,长度表示切片当前拥有的元素个数,而容量则是切片能够继续增加元素的上限。

当你创建一个切片时,实际上是在内存中找了一片空地,这片地方就是一个数组,切片的指针指向这个数组的开始。如果你对切片进行修改,其实是直接在这个数组上操作。更有趣的是,如果你再创建一个切片,它也可以指向同一个数组的不同部分,这样就实现了切片之间的共享。因为切片只是对数组的一种引用,所以修改一个切片会影响到其他引用同一数组的切片。

当切片的元素个数不够用时,切片就会像智能剪刀一样自动找一片更大的空地,搬迁原来的元素,并把指针、长度和容量都更新,保证切片继续灵活使用。这种动态的、自动管理内存的特性使得切片成为Go语言中强大而灵活的数据结构。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

风不归Alkaid

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

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

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

打赏作者

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

抵扣说明:

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

余额充值