当前位置: 首页 > article >正文

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],拷贝不受影响
}


http://www.kler.cn/a/326912.html

相关文章:

  • AI - 使用LangChain构建简单LLM应用程序
  • 一二三应用开发平台自定义查询设计与实现系列3——通用化重构
  • 【Linux】命令行参数 | 环境变量
  • C#如何封装将函数封装为接口dll?
  • 使用Mac如何才能提高OCR与翻译的效率
  • Java 实现接口幂等的九种方法:确保系统稳定性与数据一致性
  • 正则表达式和re模块
  • 递归算法介绍和【题解】——数楼梯
  • JS设计模式之享元模式:优化对象内存占用的利器
  • 新手教学系列——系统模块划分原则:如何让系统架构更加灵活与高效
  • 解决端口被占用
  • RIP路由(已被淘汰)
  • .net Framework 4.6 WebAPI 使用Hangfire
  • DRF实操——项目部署
  • 支持老挝语语音识别翻译,对着说话的翻译器《老挝语翻译通》app
  • Spring IoC笔记
  • 【Spring】lombok、dbUtil插件应用
  • 【SQL】筛选字符串与正则表达式
  • 07_矩形圆形绘制
  • 责任链模式优化 文章发布的接口(长度验证,敏感词验证,图片验证等环节) 代码,示例
  • Linux云计算 |【第四阶段】RDBMS1-DAY1
  • EZUIKit.js萤石云vue项目使用
  • Golang plugin包教程:创建与管理插
  • MacOS多桌面调度快捷键
  • 1.1.4 计算机网络的分类
  • 一篇文章快速学会docker容器技术