golang学习笔记17-切片

注:本人已有C,C++,Python基础,只写本人认为的重点。

一、切片的定义,与数组的区别

切片(slice)是go中非常重要的数据结构,它与数组(array)有一些关键的区别:
1.数组的大小是固定的,在定义时就必须指定,不能动态改变。而切片是动态的,可以根据需要扩展。
2.数组是值类型,传递数组时会复制整个数组。切片是引用类型,传递的是指向底层数组的指针,因此切片的操作会影响原始数据
3.数组在声明时分配内存,其大小固定。切片在运行时可以根据需要动态分配内存,并且可以通过append函数增加元素,go会自动处理内存分配。
切片的本质是对数组一个连续片段的引用,这个片段可以是整个数组,或者是由起始和终止索引标识的一些项的子集。需要注意的是,终止索引标识的项不包括在切片内。切片提供了一个相关数组的动态窗口。切片的语法:var 切片名 []类型 = 数组的一个片段引用。示例如下:

package main

import "fmt"

func main() {
	// 定义一个数组
	arr := [5]int{10, 20, 30, 40, 50}

	// 创建切片,引用数组的一个片段,注意区间是左闭右开
	var slice []int = arr[1:4] // 包含索引 1 到 3 的元素,结果是 [20, 30, 40]

	// 输出数组和切片
	fmt.Println("数组:", arr)   // 输出: 数组: [10 20 30 40 50]
	fmt.Println("切片:", slice) // 输出: 切片: [20 30 40]

	// 修改切片中的元素
	slice[0] = 99
	fmt.Println("修改后的数组:", arr)   // 输出: 修改后的数组: [10 99 30 40 50]
	fmt.Println("修改后的切片:", slice) // 输出: 修改后的切片: [99 30 40]
}

上述示例说明访问slice下标即访问数组元素,用指针的概念解释就是:slice是数组的头指针,用下标访问slice等效于取引用,即slice[i]=(*slice)[i]。所以slice可通过下标修改原数组,这就是为什么最后arr和slice都改变的原因。

二、容量与长度

切片有3个字段的数据结构:一个是指向底层数组的指针,一个是切片的长度,一个是切片的容量。长度是切片中实际存储的元素数,容量是切片能够存储的最大元素数,即从切片的起始位置到底层数组的末尾的元素数,它表示在不重新分配内存的情况下,切片可以扩展到的最大长度。切片slice的长度可通过len(slice) 获取,容量可通过cap(slice)获取。当用append函数向切片添加元素时,如果长度超过容量,go会自动分配新的底层数组,复制原有元素,容量也会相应增加。示例如下:

package main

import "fmt"

func main() {
	slice := []int{1, 2, 3}
	fmt.Println("长度:", len(slice)) // 输出: 长度: 3
	fmt.Println("容量:", cap(slice)) // 输出: 容量: 3

	slice = append(slice, 4, 5) // 添加元素

	fmt.Println("新长度:", len(slice)) // 输出: 新长度: 5
	fmt.Println("新容量:", cap(slice)) // 输出: 新容量: 6(可能会根据实现变化)
}

三、创建方式

package main

import "fmt"

func main() {
	// 1. 定义一个切片,让它引用一个已创建的数组
	arr := [5]int{10, 20, 30, 40, 50} // 创建一个数组
	slice1 := arr[1:4]                // 切片引用数组的一部分,包含索引 1 到 3 的元素
	fmt.Println("切片1:", slice1)       // 输出: 切片1: [20 30 40]

	// 2. 使用 make 内置函数创建切片
	slice2 := make([]int, 3)            // 创建一个长度为 3 的切片,初始元素为 0
	fmt.Println("切片2:", slice2)         // 输出: 切片2: [0 0 0]
	fmt.Println("切片2的容量:", cap(slice2)) // 输出: 切片2的容量: 3

	// 可以指定容量
	slice3 := make([]int, 2, 5)         // 创建一个长度为 2,容量为 5 的切片
	fmt.Println("切片3:", slice3)         // 输出: 切片3: [0 0]
	fmt.Println("切片3的容量:", cap(slice3)) // 输出: 切片3的容量: 5

	// 3. 直接指定具体数组初始化切片
	slice4 := []int{1, 2, 3, 4, 5} // 直接定义并初始化切片
	fmt.Println("切片4:", slice4)    // 输出: 切片4: [1 2 3 4 5]
}

四、遍历

package main

import "fmt"

func main() {
	// 创建一个切片
	slice := []int{10, 20, 30, 40, 50}

	fmt.Println("常规 for 循环遍历:")
	for i := 0; i < len(slice); i++ { // 使用索引遍历切片
		fmt.Printf("索引 %d 的元素: %d\n", i, slice[i]) // 通过索引访问元素
	}

	fmt.Println("\nfor-range 遍历:")
	for index, value := range slice { // range 返回索引和元素值
		fmt.Printf("索引 %d 的元素: %d\n", index, value) // 直接使用元素值
	}
}

五、一些细节

package main

import "fmt"

func main() {
	arr := [5]int{1, 2, 3, 4, 5}
	slice1 := arr[1:4]
	// 【1】切片使用不能越界
	fmt.Println("访问slice1的第一个元素:", slice1[0]) // 输出: 2
	// fmt.Println(slice1[5])// 取消注释将导致运行时错误:索引越界(index out of range)
	// 【2】切片引用数组的三种简写方式
	// 1. 从头到某个索引
	slice2 := arr[0:3]                 // 包含元素 1, 2, 3
	fmt.Println("引用arr[0:3]:", slice2) // 输出: [1 2 3]

	// 2. 从某个索引到末尾
	slice3 := arr[1:]                 // 从索引 1 到末尾(包含元素 2, 3, 4, 5)
	fmt.Println("引用arr[1:]:", slice3) // 输出: [2 3 4 5]

	// 3. 从开始到末尾
	slice4 := arr[:]                 // 包含所有元素
	fmt.Println("引用arr[:]:", slice4) // 输出: [1 2 3 4 5]

	// 【4】切片可以继续切片
	slice5 := slice4[1:3]                // 从 slice4 中切出元素 2 和 3
	fmt.Println("从slice4切出的切片:", slice5) // 输出: [2 3]

	// 【5】切片的拷贝
	// 使用 copy 函数拷贝切片
	slice6 := make([]int, len(slice4))      // 创建一个新的切片以存放拷贝
	copy(slice6, slice4)                    // 拷贝 slice4 的内容到 slice6
	fmt.Println("slice4的拷贝slice6:", slice6) // 输出: [1 2 3 4 5]

	// 修改原切片,观察拷贝是否影响
	slice4[0] = 100
	fmt.Println("修改slice4后的slice6:", slice6) // 输出: [1 2 3 4 5],拷贝不受影响
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

技术卷

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

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

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

打赏作者

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

抵扣说明:

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

余额充值