南阳建设网站,中国北京出啥大事了,如何做二级网站,想自己做网站做推广名人说#xff1a;莫愁千里路#xff0c;自有到来风。 ——钱珝 创作者#xff1a;Code_流苏(CSDN)#xff08;一个喜欢古诗词和编程的Coder#x1f60a;#xff09; 目录 1. 数组① 什么是数组② 数组的声明③ 初始化数组的几种方式④ 遍历数组元素⑤ 数组为值类型⑥ 数… 名人说莫愁千里路自有到来风。 ——钱珝 创作者Code_流苏(CSDN)一个喜欢古诗词和编程的Coder 目录 1. 数组① 什么是数组② 数组的声明③ 初始化数组的几种方式④ 遍历数组元素⑤ 数组为值类型⑥ 数组的排序⑦ 多维数组 2. 切片动态数组的抽象① 定义切片② make来创建切片③ 切片扩容④ 遍历切片⑤ 扩容的内存分析 3、小结 Go语言中的数组和切片是管理集合数据的重要方式具有不同的特性和用途。在这篇博客中我们将深入探讨数组和切片的相关知识点包括它们的定义、操作方法及其内部机制。
1. 数组
① 什么是数组
数组是同一种数据类型元素的集合。在Go语言中数组是值类型数组的长度在声明时就已经确定且不能改变。关于数组可以这样理解有一个收纳盒专门用于收集同一类书的但是这个收纳盒容量是有限的自从这个收纳盒生产出来大小就已经确定了而且不能再改变而这个收纳同一类型的盒子也就是数组。
② 数组的声明
数组声明的基本语法格式为
var arr [n]Type其中n表示数组的长度Type表示数组中元素的类型。
案例数组的定义、赋值、打印
package mainimport fmt/*
数组相同类型数据的集合
*/// 数组
func main() {//array 数组定义变量//数组也是一种数据类型//数组的定义[数组的大小size] 变量的类型//意思为定义一组该类型的数组集合大小为size最多可以保存size个数var arr1 [5]int//[0,0,0,0,0]//给数组赋值下标index从0开始arr1[0] 100arr1[1] 200arr1[2] 300arr1[3] 400arr1[4] 500//打印数组fmt.Println(arr1)//取出数组中的某个元素fmt.Println(arr1[1])//数组中常用的方法 len()获取数组长度 cap()获取数组容量fmt.Println(数组的长度, len(arr1))fmt.Println(数组的容量, cap(arr1))//修改数组的值index 1 代表数组中的第二个数据arr1[1] 50fmt.Println(修改后的数组, arr1)fmt.Println(arr1[1])
}③ 初始化数组的几种方式
指定所有元素初始化
arr : [5]int{1, 2, 3, 4, 5}部分元素初始化未初始化元素为零值
arr : [5]int{1, 2} // 其余元素初始化为0根据初始化值自动确定数组长度
arr : [...]int{1, 2, 3, 4, 5}案例数组初始化
package mainimport fmt// 数组的赋值初始化
func main() {//定义时初始化var arr1 [5]int{1, 2, 3, 4, 5}fmt.Println(arr1)//快速初始化 :arr2 : [5]int{1, 2, 3, 4, 5}fmt.Println(arr2)//需要注意的点//数据假如来自用户, 但不知道究竟有多少数据//... 写法//Go的编译器会根据数组的长度来给 ...赋值自动推导长度//注意点数组不是无限长也是固定大小大小取决于数组元素的个数var arr3 [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9}fmt.Println(len(arr3))fmt.Println(arr3)//数组默认值给其中的几个位置赋值//可使用{index:值}的方法var arr4 [10]intarr4 [10]int{1: 100, 5: 500}fmt.Println(arr4) // [0 100 0 0 0 500 0 0 0 0]
}④ 遍历数组元素
遍历数组元素可以使用for循环或for range循环。
arr : [...]int{1, 2, 3, 4, 5}
for i : 0; i len(arr); i {fmt.Println(arr[i])
}或者
for index, value : range arr {fmt.Printf(Index: %d, Value: %d\n, index, value)
}上面那种方式大家可能比较熟悉C、C等语言中都是类似的写法但是下面这种方式如果没学过python等语言可能了解会少一些因此在个人在这里解释一下
这段代码实现了什么
for循环遍历arr数组或切片。
这段代码该怎么理解
可以这样看每次迭代for循环会将当前元素的索引赋值给index变量将当前元素的值赋值给value变量然后执行循环体内的语句。而其中的range arr就是负责生成索引和对应值的部分比如在循环刚开始数组中index0arr[index] 1那么所生成的index值 0value值 arr[index] 1。
让我们逐步解析这段代码 for index, value : range arr { ... } for关键字开始一个循环。index, value是每次迭代时range表达式返回的两个值。index是当前元素的索引value是当前元素的值。range arr表示对arr进行遍历。arr可以是数组或切片。range每次迭代都会返回当前元素的索引和值。{ ... }中的代码是循环体会对arr中的每个元素执行一次。 fmt.Printf(Index: %d, Value: %d\n, index, value) 这行代码在循环体内使用fmt.Printf函数打印当前元素的索引和值。%d是格式化字符串用于表示整数。\n是换行符。在每次迭代中index和value将被设置为当前遍历到的元素的索引和值然后将它们打印出来。
举个具体的例子如果arr是一个包含三个元素的切片[10, 20, 30]那么输出将会是
Index: 0, Value: 10
Index: 1, Value: 20
Index: 2, Value: 30因此一言以蔽之这段代码的作用就是遍历数组或切片arr并打印出每个元素的索引和值。
⑤ 数组为值类型
数组在Go语言中是值类型当数组作为函数参数传递时传递的是数组的副本而不是其引用。
func modifyArray(arr [5]int) {arr[0] 10 // 这里修改的是数组的副本
}案例数组值类型 package mainimport fmt// 数组是值类型所有赋值后的对象修改值不影响原来的对象func main() {//数组 [size]typearr1 : [4]int{1, 2, 3, 4}arr2 : [5]string{yueliusu, zilihuakai}fmt.Printf(%T\n, arr1) //[4]intfmt.Printf(%T\n, arr2) //[5]string//数组的值传递和int等基本类型一致arr3 : arr1fmt.Println(arr1)fmt.Println(arr3)//修改arr3观察arr1是否会变化arr3[0] 12fmt.Println(arr1)fmt.Println(arr3) // 数组是值传递拷贝一个新的内存空间}⑥ 数组的排序
可以自定义实现也可以使用sort包可以对数组进行排序。例如对一个整型数组进行升序排序
package main
import fmt
import sortfunc main() {arr : [5]int{3, 1, 4, 5, 2}sort.Ints(arr[:]) // 将数组转换为切片fmt.Println(arr)
}案例自己实现排序算法冒泡排序
package mainimport fmt// 冒泡每次筛选出一个最大或者最小的数.
/*
index 0 1 2 3 4
value 12 99 79 48 55
*/
// 冒泡排序逻辑两两比较大的往后移或者前移。 大
// 第一轮 12 79 48 55 99 // 5
// 第二轮 12 48 55 79 99 // 4
// 第三轮 12 48 55 79 99 // 3 //
// 第四轮 12 48 55 79 99 //
// 第五轮 12 48 55 79 99// 代码实践
/*// 两个数判断如果一个数大则交换位置大放到后面if arr[x] arr[x1] {arr[x], arr[x1] arr[x1],arr[x]}// 多轮判断for 循环次数 【数组大小】
*/
func main() {arr : [...]int{12, 99, 79, 48, 55, 1, 110, 111, 23, 44, 21, 312, 123, 21, 312}fmt.Println(初始数组, arr)//冒泡排序//1、多少轮for i : 1; i len(arr); i {//2、筛选出来最大数for j : 0; j len(arr)-i; j {//降序排序比较大小改变升降序只需要改变符号即可if arr[j] arr[j1] {arr[j], arr[j1] arr[j1], arr[j] //平行赋值无需第三者临时变量进行交换}}}fmt.Println(arr)
}⑦ 多维数组
多维数组可以通过嵌套数组来声明俗称套娃。
一维线性
二维表格数组套数组
三维立体数组套数组套数组
多维继续嵌套
var multiArr [2][3]int
multiArr[0] [3]int{1, 2, 3}
multiArr[1] [3]int{4, 5, 6}案例多维数组
package mainimport (fmt
)func main() {//多维数组//定义一个二维数组arr : [3][4]int{{0, 1, 0, 0}, //arr[0]{0, 0, 1, 0}, //arr[1]{0, 2, 0, 0}, //arr[2]}//二维数组fmt.Println(arr[0])fmt.Println(arr[0][1])fmt.Println(----------)//遍历二维数组for i : 0; i len(arr); i {for j : 0; j len(arr); j {fmt.Println(arr[i][j])}}// for rangefor i, v : range arr {fmt.Println(i, v)}
}2. 切片动态数组的抽象
① 定义切片
切片是对数组的封装它提供了一个可动态扩展的序列。切片不存储任何数据它**只是对底层数组的引用。**那么它与数组相同吗
显然切片与数组是不同的切片的长度是动态的可以根据需要扩展或缩减。切片背后实际上是对数组的引用切片是引用类型。
var slice []int补充一下关于切片的基础概念
底层数组每个切片都指向一个底层的数组切片通过指针引用数组的一部分元素。长度Length切片的长度是它所包含的元素个数。可以通过内置的len函数获取。容量Capacity切片的容量是从其起始元素到底层数组末尾元素的个数。可以通过内置的cap函数获取。
案例切片的定义
package mainimport fmt// 定义切片
func main() {arr : [4]int{1, 2, 3, 4} //数组定长fmt.Println(arr)var s1 []int //切片不定长长度是可变的fmt.Println(s1)//切片空片段初始的切片中默认为nilif s1 nil {fmt.Println(切片是空的)}s2 : []int{1, 2, 3, 4} //切片 不定长fmt.Println(s2)fmt.Printf(%T,%T\n, arr, s2) //[4]int,[]intfmt.Println(s2[1])
}② make来创建切片
通过make函数创建切片可以指定切片的长度和容量。
//例如创建一个长度为5容量为10的切片
slice : make([]int, 5, 10)案例make创建切片
package mainimport fmtfunc main() {//make()//make([]Type, length, capacity) //创建一个切片长度容量s1 : make([]int, 5, 10)fmt.Println(s1)fmt.Println(len(s1), cap(s1))//思考容量为10长度为5可以存放6个数据吗s1[0] 10//s1[7] 200 //runtime error: index out of range [7] with length 5//切片的底层还是数组 [0 0 0 0 0] [2000]//直接去赋值是不行的不要用常规的惯性想法来考虑不加思索说出答案思考的过程很重要。fmt.Println(s1)}③ 切片扩容
当向切片追加元素超过其容量时Go会自动进行扩容一般会翻倍增加比如说原本容量为2你要放3个放不下了它会自动扩容为4依次类推扩容为8、16等
slice : make([]int, 0, 2)
for i : 0; i 10; i {slice append(slice, i)
}案例切片扩容自动翻倍
package mainimport fmtfunc main() {//这段代码意味着创建了一个整型切片初始长度为0容量为5s1 : make([]int, 0, 5)fmt.Println(s1)//切片扩容append()s1 append(s1, 1, 2)fmt.Println(s1)//问题容量只有5个那能放超过5个的吗当然切片是会自动扩容的s1 append(s1, 2, 3, 4, 5, 6, 7, 8, 9)fmt.Println(s1)s2 : []int{100, 200, 300, 400}// slice append(slice, anotherSlice...)// ... 可变参数 ...xxx// [...] 根据长度变化数组的大小定义// anotherSlice... , slice...解构可以直接获取到slice中的所有元素// s2... {100,200,300,400}s1 append(s1, s2...)//遍历切片for i : 0; i len(s1); i {fmt.Println(s1[i])}for i : range s1 {fmt.Println(s1[i])}}④ 遍历切片
切片的遍历方式和数组相同可以使用for循环或for range循环因为本质上切片相当于是引用了数组数组是一种数据类型切片引用它扩展了它因此遍历方式没太大差别。
slice : []int{1, 2, 3, 4, 5}
for _, value : range slice {fmt.Println(value)
}⑤ 扩容的内存分析
切片的扩容会导致内存重新分配及旧数据的复制这可能影响性能。合理规划初始容量可以减少扩容的次数提升性能。
slice : make([]int, 0, 1024) // 提前分配足够的容量案例切片扩容的内存变化
package mainimport fmt// 切片扩容的内存分析
// 结论
// 1、每个切片引用了一个底层的数组
// 2、切片本身不存储任何数据都是底层的数组来存储的所以修改了切片也就是修改了这个数组中的数据
// 3、向切片中添加数据的时候如果没有超过容量直接添加如果超过了容量会自动扩容成倍增加copy
// - 分析程序的原理
// - 看源码
// 4、切片一旦扩容就是重新指向一个新的底层数组func main() {//1、cap每次是成倍增加的//2、只要容量扩容后地址就会发生变化s1 : []int{1, 2, 3}fmt.Println(s1)fmt.Printf(len%d,cap:%d\n, len(s1), cap(s1)) //len:3,cap:3fmt.Printf(%p\n, s1) //0xc000016108s1 append(s1, 4, 5)fmt.Printf(len:%d,cap:%d\n, len(s1), cap(s1)) //len:5,cap:6fmt.Printf(%p\n, s1) //0xc000010390s1 append(s1, 6, 7, 8)fmt.Printf(len:%d,cap:%d\n, len(s1), cap(s1)) //len:8,cap:12fmt.Printf(%p\n, s1) //0xc00005e060s1 append(s1, 9, 10)fmt.Printf(len:%d,cap:%d\n, len(s1), cap(s1)) //len:10,cap:12fmt.Printf(%p\n, s1) //0xc00005e060s1 append(s1, 11, 12, 13, 14)fmt.Printf(len:%d,cap:%d\n, len(s1), cap(s1)) //len:14,cap:24fmt.Printf(%p\n, s1) //0xc00010c000
}案例2copy方法
package mainimport fmt// copy方法
func main() {numbers : []int{1, 2, 3}fmt.Printf(len%d,cap%d,slice%v\n, len(numbers), cap(numbers), numbers)//方法一直接使用make创建切片扩容numbers2 : make([]int, len(numbers), cap(numbers)*2)//将原来的底层数据的值拷贝到新的数组中//func copy(dst,src []Type)intcopy(numbers2, numbers)fmt.Printf(len%d,cap%d,slice%v\n, len(numbers2), cap(numbers2), numbers2)
}3、小结 数组 一组数数组是值传递的创建数组 [size]int数组的大小事不可变的二维数组数组套数组冒泡排序 切片 切片本事是不存在数据的底层是指向了一个数组如果我们存放的数据大于切片的容量在底层就会扩容 copy 1024 1.25倍 很感谢你能看到这里如有相关疑问还请下方评论留言。 Code_流苏(CSDN)一个喜欢古诗词和编程的Coder 希望本篇内容能对大家有所帮助如果大家喜欢的话请动动手点个赞和关注吧非常感谢你们的支持