golang之数组和切片

简介: 本文主要介绍了从 Java 转向 Golang 的开发者在数组和切片上的常见困惑。文章详细解析了数组和切片的底层实现、声明方式、内存结构、初始化方法以及使用场景,重点强调了数组是不可变的值类型,而切片是可变的引用类型,并通过大量代码示例讲解了它们的用法与区别。此外,还探讨了 new() 和 make() 函数的不同用途,帮助开发者更好地理解 Golang 中的数据结构机制。

对于一个从java转golang的开发者而言,数组和切片是最容易弄晕的地方。往往傻傻的分不清数组和切片的区别。

其实这两个数据结构从底层的实现来说并没有什么差异,只是数组是不可变化的(这里不可变化并不是说其是常量),而切片是可变的。

  • 数组
  • 数组的声明
    数组的声明格式中必须指定数组的长度,而且指定之后长度就不可以变化,而且不能像java中一样数组的长度使用一个变量填充
    声明的格式是:
  • 体验AI代码助手
  • 代码解读
  • 复制代码
var identifier [len]type
  • 例如:
  • 体验AI代码助手
  • 代码解读
  • 复制代码
var arr1 [5]int
  • 以为数组是一种值类型,除了这种声明方式以外还可以使用new()函数来声明
  • 体验AI代码助手
  • 代码解读
  • 复制代码
var arr1 = new([5]int) 。
  • 数组的内存结构
    数组在内存的结构是:
    有一点要注意的是,在go中如果声明一个数组的时候没有给他初始化,那么他会将每个元素都初始化为零,此时如果打印arr1 其值为[0,0,0,0,0]
    因为数组是值类型,所以在使用数组作为函数的参数的时候,函数会拷贝整个数组生成一个副本。所以如果为了效率考虑,可以传递数组的地址,或者直接使用切片作为参数。
  • 数组的循环方式
    数组循环的方式有两种,第一种是for循环,
  • 体验AI代码助手
  • 代码解读
  • 复制代码
for i:=0; i < len(arr1); i++{
arr1[i] = ...
}
  • 第二种是for-range循环
  • 体验AI代码助手
  • 代码解读
  • 复制代码
// index为数组的索引,value为索引对应的值
for index,value:= range arr1 {
...
}
  • 数组初始化
    第一种:
  • 体验AI代码助手
  • 代码解读
  • 复制代码
var arrAge = [5]int{18, 20, 15, 22, 16}
  • 第二种:
  • 体验AI代码助手
  • 代码解读
  • 复制代码
var arrLazy = [...]int{5, 6, 7, 8, 22}
  • ... 可同样可以忽略,从技术上说它们其实变化成了切片。 第三种: key: value
  • 体验AI代码助手
  • 代码解读
  • 复制代码
var arrKeyValue = [5]string{3: "Chris", 4: "Ron"}
  • 这种赋值方法直接指定了数组所在的索引,所以只有索引 3 和 4 被赋予实际的值,其他元素都被设置为空的字符串,所以输出结果为:
  • 体验AI代码助手
  • 代码解读
  • 复制代码
Person at 0 is
Person at 1 is
Person at 2 is
Person at 3 is Chris
Person at 4 is Ron
  • 切片
  1. 切片是数组的一个引用,因此切片是引用类型,在进行传递时,遵守引用类型的传递机制。
  2. 切片的使用和数组类似,遍历切片,访问切片的元素和求切片的长度len(slice)都是一样。
  3. 切片的长度是可以变化的。因此是一个动态变化的数组。
  • 创建切片
  • 普通声明不初始化
    声明切片的格式是: var slice []type (不需要说明长度)。 一个切片在未初始化之前默认为 nil,长度为 0,此时的切片虽然声明但是不可使用
  • 使用make初始化
  • 体验AI代码助手
  • 代码解读
  • 复制代码
// 初始化一个长度为5容量为5的切片 [0,0,0,0,0]
var slice = make([]int,5,5)
//  初始化一个长度为5容量为5的切片 [0,0,0,0,0]
var slice = make([]int,5)
  • 使用数组或者切片初始化一个切片
  • 体验AI代码助手
  • 代码解读
  • 复制代码
var arr = [5]int{1,2,3,4,5}
// 初始化一个切片,切片的值从数组的下标1开始到2结束(不包括2)
var slice = arr[1:2]
// 从数组下标n开始到m结束(不包括m),也可以使用切片赋值切片
var slice = arr[n,m]
  • 切片的遍历
    切片的遍历和数组一致
  • 切片的内存模型
  • 切片的扩容、拷贝和删除切片增加内容有点像Java中的ArrayList,ArrayList使用他本身的add方法进行增加,而切片则是使用一个函数叫做append。
  • 体验AI代码助手
  • 代码解读
  • 复制代码
var slice = make([]int,5,5)
// 向切片中添加一个10
slice = append(slice,10)
  • 当添加的长度超过容量的时候,那么这个时候切片就要开始扩容了,这个扩容的方法是append自带的一种检查扩容机制,就像List的add方法中检查grow一样那么切片是怎么扩容的呢?
  • 首先,需要判断新申请的容量是否大于2倍旧的容量,如果大于,则最终容量就是新申请的容量
  • 否则判断,切片的长度是否小于1024,小于则最终容量就是旧容量的两倍;否则最终容量在旧容量的基础上循环增加原来的1/4,直到最终容量大于等于新申请的容量
  • 如果最终容量计算值溢出,则最终容量就是新申请的容量
  • 需要注意的是,切片扩容会根据切片元素中的不容类型做不容的处理,而且每次切片扩容之后其实是产生一个新的切片,将旧有的切片复制到新的中这种处理和ArrayList的扩容方式是一样的
    如下,这是源码中有关扩容策略的一部分源码,此处只包含了见到那的扩容策略,源码中还包含了其他的处理,源码位置:src/runtime/slice.go
  • 体验AI代码助手
  • 代码解读
  • 复制代码
newcap := old.cap
	doublecap := newcap + newcap
	if cap > doublecap {
		newcap = cap
	} else {
		if old.len < 1024 {
			newcap = doublecap
		} else {
			// Check 0 < newcap to detect overflow
			// and prevent an infinite loop.
			for 0 < newcap && newcap < cap {
				newcap += newcap / 4
			}
			// Set newcap to the requested cap when
			// the newcap calculation overflowed.
			if newcap <= 0 {
				newcap = cap
			}
		}
	}
  • 拷贝一个切片到另外一个切片可以使用copy()函数,copy函数将返回成功复制的元素的个数,等于两个slice中较小的长度,所以我们不用担心覆盖会超出目标切片的范围。
  • 体验AI代码助手
  • 代码解读
  • 复制代码
slice1:=[]int{1,2,3,4,5}
slice2:=[]int{5,4,3}
//只会复制slice1的前3个元素到slice2中
copy(slice2,slice1)
//只会复制slice2的3个元素到slice1的前3个位置
copy(slice1,slice2)
  • copy函数的第一个参数是要复制的目标切片,第二个参数是源切片,两个slice可以共享同一个底层数组,甚至有重叠也没有问题。
    切片没有删除方法,所以如果想要删除切片就需要使用我们上面说的使用数组或者切片初始化一个切片
  • new()和make()的区别
    看起来二者没有什么区别,都在堆上分配内存,但是它们的行为不同,适用于不同的类型。 new(T) 为每个新的类型T分配一片内存,初始化为 0 并且返回类型为*T的内存地址。这种方法 返回一 个指向类型为 T,值为 0 的地址的指针,它适用于值类型如数组和结构体。 make(T) 返回一个类型为 T 的初始值,它只适用于3种内建的引用类型:切片、map 和 channel。
    所以new()只是开辟了一个内存,如果不是基本类型那么这个类型并不能直接使用;而make()会初开辟一个内存并初始化。


转载来源:https://juejin.cn/post/6850418116598595592

相关文章
|
Go
Golang切片copy踩坑
Golang切片copy踩坑
177 0
|
7月前
|
存储 Java Go
【Golang】(3)条件判断与循环?切片和数组的关系?映射表与Map?三组关系傻傻分不清?本文带你了解基本的复杂类型与执行判断语句
在Go中,条件控制语句总共有三种if、switch、select。循环只有for,不过for可以充当while使用。如果想要了解这些知识点,初学者进入文章中来感受吧!
280 2
|
存储 Java Go
Golang 语言中数组和切片的区别是什么?
Golang 语言中数组和切片的区别是什么?
227 0
|
存储 大数据 Go
100天精通Golang(基础入门篇)——第11天:深入解析Go语言中的切片(Slice)及常用函数应用
100天精通Golang(基础入门篇)——第11天:深入解析Go语言中的切片(Slice)及常用函数应用
441 0
|
Go
Golang语言之切片(slice)快速入门篇
这篇文章是关于Go语言中切片(slice)的快速入门教程,详细介绍了切片的概念、定义方式、遍历、扩容机制、使用注意事项以及相关练习题。
577 6
|
Go
【golang】golang 字符串切片排序
【golang】golang 字符串切片排序
218 1
|
监控 Serverless Go
Golang 开发函数计算问题之Go 语言中切片扩容时需要拷贝原数组中的数据如何解决
Golang 开发函数计算问题之Go 语言中切片扩容时需要拷贝原数组中的数据如何解决
173 0
|
程序员 Go
第七章 Golang数组和切片
第七章 Golang数组和切片
181 2
|
Go C++ Java
C/C++每日一练(20230408) 删除无效括号、合并K个升序链表、四数之和
C/C++每日一练(20230408) 删除无效括号、合并K个升序链表、四数之和
198 0
C/C++每日一练(20230408) 删除无效括号、合并K个升序链表、四数之和
|
Go 索引
Golang深入浅出之-切片(Slices)入门:创建、操作与扩容机制
【4月更文挑战第20天】Go语言中的切片是动态数组,提供灵活的操作和自动扩容。本文介绍了切片的创建(通过`make()`、数组创建和切片字面量)、基本操作(索引访问、切片、赋值追加和遍历)以及扩容机制(首次和后续扩容策略)。此外,还强调了切片与底层数组的关系、切片越界问题、`append()`的使用以及理解切片的关键点,帮助提升Go编程效率和代码质量。
568 0

热门文章

最新文章

推荐镜像

更多