[go]Slice 切片原理

简介: 本文详细介绍了Go语言中的切片(slice)数据结构,包括其定义、创建方式、扩容机制及常见操作。切片是一种动态数组,依托底层数组实现,具有灵活的扩容和传递特性。文章解析了切片的内部结构(包含指向底层数组的指针、长度和容量),并探讨了通过`make`创建切片、基于数组生成切片以及切片扩容的规则。此外,还分析了`append`函数的工作原理及其可能引发的扩容问题,以及切片拷贝时需要注意的细节。最后,通过典型面试题深入讲解了切片在函数间传递时的行为特点,帮助读者更好地理解和使用Go语言中的切片。

go 切片

在go 语言的世界中,切片是一个很常用的数据结构,同时也有很多的坑,在面试中十个有八个面试官会问到,本文主要整理下切片数据结构、创建方式、扩容方式、常见面试题等。

slice 又称动态数组,依托数组实现,可以方便地进行扩容、传递等,实际使用中比数组更加灵活。

切片与底层数组共享一个内存地址,当切片的值改变时,会影响底层数组的值也一起改变,当切片扩容时会重新申请内存地址,此时切片不会影响底层数组

slice 依托数组实现,底层数组对用户屏蔽,在底层数组容量不足时可以实现自动重新分配并生成新的slice。

数据结构

go

体验AI代码助手

代码解读

复制代码

type slice struct {
    array unsafe.Pointer    // 指向底层数组的地址
    len int                 // 长度  
    cap int                 // 容量
}

创建切片

  • 使用make

使用make 创建slice ,可以指定长度和容量,创建时底层会分配一个数组,数组的长度即容量 例如slice :=make([]int,5,10),底层指向的就是一个长度为10的数组,该slice 长度为5 ,可以使用slice[0]~slice[4]来操作元素,capacity为10,表示后续想slice添加新元素时不必重新分配内存,直接使用预留内存

  • 使用数组创建

使用数组创建slice ,slice与原数组共用一部分内存slice:=array[5:7]切片从数组array[5]开始到array[7]结束,切片长度为2,数组后面的内容都作为切片的预留内存

slice 扩容

使用append 想slice 追加元素时,如果slice 空间不足,将会触发slice 扩容,扩容实际上就是重新分配一个更大的内存,将原slice 的数据拷贝进新的slice ,然后返回新的slice 扩容原则

  • 如果原Slice容量小于1024,则新Slice容量将扩大为原来的2倍
  • 如果原slice 大于等于1024 扩大为原来的1.25倍

append 的实现步骤

  • 假如Slice容量够用,则将新元素追加进去,Slice.len++,返回原Slice
  • 原Slice容量不够,则将Slice先扩容,扩容后得到新Slice
  • 将新元素追加进新Slice,Slice.len++,返回新的Slice。

slice copy

使用copy()内置函数拷贝两个切片时,会将源切片的数据逐个拷贝到目的切片指向的数组中,拷贝数量取两个切片长度的最小值。

例如长度为10的切片拷贝到长度为5的切片时,将会拷贝5个元素,也就是说copy 的过程中不会发生扩容 // 可以使用这个特性来比较两个数的大小

go

体验AI代码助手

代码解读

复制代码

min= copy(make([]struct{},a),make([]struct{},b))

特殊切片

根据数组或者切片生成新切片一般使用slice:=array[start:end],这种新生成的切片没有指定容量,实际上新切片容量为从start 开始到array 结束 比如如下两个切片,长度和容量都是一致的,使用共同的内存地址

go

体验AI代码助手

代码解读

复制代码

sliceA := make([]int,5,10)
sliceB := sliceA[0:5]

根据数组或者切片生成新切片还有另一种写法,可以指定新切片的容量slice := array[start:end:cap],cap不能超过原切片的实际值

go

体验AI代码助手

代码解读

复制代码

sliceA := make([]int,5,10)
sliceB := sliceA[0:5]
sliceC := sliceA[0:5:5]

总结

  • 创建切片时可根据实际需要预分配容量,尽量避免追加过程中扩容操作,有利于提升性能;
  • 切片拷贝时需要判断实际拷贝的元素个数
  • 谨慎使用多个切片操作同一个数组,以防读写冲突
  • 每个切片都指向一个底层数组
  • 每个切片都保存了当前切片的长度、底层数组可用容量
  • 使用len()计算切片长度时间复杂度为O(1),不需要遍历切片
  • 使用cap()计算切片容量时间复杂度为O(1),不需要遍历切片
  • 通过函数传递切片时,不会拷贝整个切片,因为切片本身只是个结构体而已
  • 使用append()向切片追加元素时有可能触发扩容,扩容后将会生成新的切片

面试题

go

体验AI代码助手

代码解读

复制代码

package main

import "fmt"

func main() {

	{
		test := []int{1, 2, 3, 4, 5}
		change2(test)
		fmt.Println(test)
	}
}

func change2(param []int) {
	param = append(param, 6)
}

输出结果为 1,2,3,4,5 解析:change2 对切片进行了扩容操作,param 是一个函数内的局部变量已经指向了新的内存地址,test指向的底层数组不变 1、go中函数传递都是值传递

2、slice、map、channel都是引用类型,即便是值传递,结构内部还是指向原来的引用对象,所以函数体内可以直接修改元素。

3、如果slice触发扩容,data会指向新的底层数组,而不指向外部的底层数组了。所以之后再修改slice,不会对外部的slice造成影响。

go

体验AI代码助手

代码解读

复制代码

package main

import "fmt"

func main() {
	{
		test := []int{1, 2, 3, 4, 5}
		change(test)
		fmt.Println(test)

	}

}

func change(param []int) {
	param = append(param[:1], param[2:]...)
}

输出结果为 1,3,4,5,5 解析,底层数组没变,append 操作把 切片前4位的值覆盖掉了


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

目录
打赏
0
2
2
0
206
分享
相关文章
Go语言切片,使用技巧与避坑指南
Go语言中的切片(Slice)是动态引用数组的高效数据结构,支持扩容与截取。本文从切片基础、常用操作到高级技巧全面解析,涵盖创建方式、`append`扩容机制、共享陷阱及安全复制等内容。通过代码示例详解切片特性,如预分配优化性能、区分`nil`与空切片、处理多维切片等。掌握这些核心知识点,可编写更高效的Go代码。
54 2
Go与Java泛型原理简介
本文介绍了Go与Java泛型的实现原理。Go通过单态化为不同类型生成函数副本,提升运行效率;而Java则采用类型擦除,将泛型转为Object类型处理,保持兼容性但牺牲部分类型安全。两种机制各有优劣,适用于不同场景。
59 24
Go语言实战案例 - 找出切片中的最大值与最小值
本案例通过实现查找整数切片中的最大值与最小值,帮助初学者掌握遍历、比较和错误处理技巧,内容涵盖算法基础、应用场景及完整代码示例,适合初学者提升编程能力。
深入理解 go sync.Map - 基本原理
本文介绍了 Go 语言中 `map` 在并发使用时的常见问题及其解决方案,重点对比了 `sync.Mutex`、`sync.RWMutex` 和 `sync.Map` 的性能差异及适用场景。文章指出,普通 `map` 不支持并发读写,容易引发错误;而 `sync.Map` 通过原子操作和优化设计,在某些场景下能显著提升性能。同时详细讲解了 `sync.Map` 的基本用法及其适合的应用环境,如读多写少或不同 goroutine 操作不同键的场景。
|
26天前
|
Go内存原理-GC原理
本文介绍了Go语言中垃圾回收(GC)机制的发展与实现原理,涵盖从标记-清除算法到三色标记法,再到三色标记加混合写屏障的演进过程,重点解析各版本GC的核心思想、优缺点及性能优化方向。
|
1月前
|
Go语言中使用切片需要注意什么?
本文详细讲解了Go语言中切片(Slice)的使用方法与注意事项。切片是对数组连续片段的引用,具有灵活的操作方式。文章从定义与初始化、长度与容量、自动扩容、共享底层数组、复制、边界检查、零值到拼接等方面展开,并配以示例代码演示。通过学习,读者可深入了解切片的工作原理及优化技巧,避免常见陷阱,提升编程效率与代码质量。
73 2
Go语言之切片的原理与用法 - 《Go语言实战指南》
切片(slice)是Go语言中用于处理变长数据集合的核心结构,基于数组的轻量级抽象,具有灵活高效的特点。切片本质是一个三元组:指向底层数组的指针、长度(len)和容量(cap)。本文详细介绍了切片的声明与初始化方式、基本操作(如访问、修改、遍历)、长度与容量的区别、自动扩容机制、共享与副本处理、引用类型特性以及常见陷阱。通过理解切片的底层原理,开发者可以更高效地使用这一数据结构,优化代码性能。
揭秘 Go 语言中空结构体的强大用法
Go 语言中的空结构体 `struct{}` 不包含任何字段,不占用内存空间。它在实际编程中有多种典型用法:1) 结合 map 实现集合(set)类型;2) 与 channel 搭配用于信号通知;3) 申请超大容量的 Slice 和 Array 以节省内存;4) 作为接口实现时明确表示不关注值。此外,需要注意的是,空结构体作为字段时可能会因内存对齐原因占用额外空间。建议将空结构体放在外层结构体的第一个字段以优化内存使用。
监控局域网其他电脑:Go 语言迪杰斯特拉算法的高效应用
在信息化时代,监控局域网成为网络管理与安全防护的关键需求。本文探讨了迪杰斯特拉(Dijkstra)算法在监控局域网中的应用,通过计算最短路径优化数据传输和故障检测。文中提供了使用Go语言实现的代码例程,展示了如何高效地进行网络监控,确保局域网的稳定运行和数据安全。迪杰斯特拉算法能减少传输延迟和带宽消耗,及时发现并处理网络故障,适用于复杂网络环境下的管理和维护。
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问