go数据结构之slice与map

news2024/11/15 1:48:06

1. 切片

1. 切片结构定义

type slice struct {
	array unsafe.Pointer
	len   int
	cap   int
}
  • array:引用的底层数组,动态数组,可以修改
    • 如果多个切片的array指针指向同一个动态数组,则它们都可以对底层这个动态数组元素进行修改。
  • len::长度
  • cap:可以理解为底层动态数组的容量
    • 当切片添加的元素超过cap后,会引发切片底层数组扩容。扩容后array指针指向可能会发生更改

2. 切片有长度

package main

import "fmt"

func main() {
	slice1 := make([]int, 0, 4)
	fmt.Println(len(slice1))
	// panic
	slice1[0] = 1
}
  • 使用下标访问切片,访问范围不能超过切片len

2. 底层动态数组可能会被多个切片引用

package main

import "fmt"

func main() {
	slice1 := make([]int, 4, 8)
	// 4   8
	fmt.Println(len(slice1), " ", cap(slice1))

	slice2 := slice1[2:]
	// 2   6
	fmt.Println(len(slice2), " ", cap(slice2))
}
  • 上述例子中两个切片引用的是同一个底层动态数组,slice1是从动态数组头部开始引用,而slice2则是从动态数组第二个元素开始引用
  • 所以slice1能对底层动态数组全部八个位置都可以修改,而slice2则只能修改底层数组的后六位下标的位置
  • 当底层动态数组只要有一个切片引用,则整个动态数组就不会被回收,即使这个切片引用的是动态数组的局部
    • 如slice2引用会导致整个动态数组八位不能回收,虽然它只能访问和修改后六位

3. 函数中,切片是值传递

package main

import "fmt"

func main() {
	s1 := make([]int, 4, 8)
	fmt.Printf("s1的指针是 %p \n", &s1)
	printPoint(s1)

	fmt.Printf("s1的底层数组是 %p \n", s1)
	printArrPoint(s1)
}
func printPoint(s2 []int) {
	fmt.Printf("s2的指针是 %p \n", &s2)
}

func printArrPoint(s2 []int) {
	fmt.Printf("s2的底层数组是 %p \n", s2)
}
//s1的指针是 0xc000008078
//s2的指针是 0xc000008090 
//s1的底层数组是 0xc000014240 
//s2的底层数组是 0xc000014240 
  • 函数传递切片的时候,其实是把切片复制了一遍
  • 但是两个切片指向了同一个底层动态数组

4. 当切片扩容时,(可能)会指向新的底层数组

package main

import "fmt"

func main() {
	s1 := make([]int, 4, 4)
	s2 := s1
	fmt.Printf("s1的底层数组是 [%p], 容量是 [%v]\n", s1, cap(s1))
	fmt.Printf("s2的底层数组是 [%p], 容量是 [%v]\n", s2, cap(s2))

	s1 = append(s1, 1)
	fmt.Printf("s1的底层数组是 [%p], 容量是 [%v]\n", s1, cap(s1))
	fmt.Printf("s2的底层数组是 [%p], 容量是 [%v]\n", s2, cap(s2))
}
//s1的底层数组是 [0xc000150020], 容量是 [4]
//s2的底层数组是 [0xc000150020], 容量是 [4]
//s1的底层数组是 [0xc0001200c0], 容量是 [8]
//s2的底层数组是 [0xc000150020], 容量是 [4]
  • 当指向同一个底层数组时,s1对数组元素的修改对s2是可见的,当s1指向新的底层数组时,s1则对数组元素的修改则对s2是不可见了,因为它俩指向了不同的底层动态数组

5. 与空比较

package main

import "fmt"

func main() {
	var a []int
	b := make([]int, 0)
	fmt.Printf("a==nil? %v \n", a == nil)
	fmt.Printf("b==nil? %v \n", b == nil)
	fmt.Printf("len(a)==0? %v \n", len(a))
	fmt.Printf("len(b)==0? %v \n", len(b))
	fmt.Printf("a的指针是 [%p]\n", &a)
	fmt.Printf("b的指针是 [%p]\n", &b)
	fmt.Printf("a的底层数组是 [%p]\n", a)
	fmt.Printf("b的底层数组是 [%p]\n", b)
}

//a==nil? true
//b==nil? false 
//len(a)==0? 0 
//len(b)==0? 0 
//a的指针是 [0xc000008078]
//b的指针是 [0xc000008090]
//a的底层数组是 [0x0]
//b的底层数组是 [0xe6b438]

  • 切片只声明未初始化则不会分配底层数组
  • 切片可以和 nil 进行比较,只有当切片底层数据指针为空时切片本身为 nil,这时候切片的长度和容量信息将是无效的。如果有切片的底层数据指针为空,但是长度和容量不为 0 的情况,那么说明切片本身已经被损坏了。

2. map

1. map底层数据结构

type hmap struct {
	count     int              // map 中键值对的数量
	flags     uint8            // map 的标志位,如是否为引用类型等
	B         uint8            // map 的桶大小的对数
	noverflow uint16           // 溢出桶的数量
	hash0     uint32           // 哈希种子值
	buckets   unsafe.Pointer   // 存储桶的指针
	oldbuckets unsafe.Pointer   // 旧桶的指针,用于扩容时的过渡
	nevacuate uintptr          // 扩容时,已迁移的桶的数量
	extra     *mapextra        // 用于存储特殊情况下的扩展信息
}
type mapextra struct {
	overflow    *[]*bmap        // 溢出桶的数组,当哈希表中的键值对数量超过某个阈值时会使用溢出桶
	oldoverflow *[]*bmap        // 旧溢出桶的数组,用于扩容时的过渡
	nextOverflow *bmap          // 链接下一个溢出桶的指针
}
type bmap struct {
	tophash [bucketCnt]uint8
}

//在编译期间会产生新的结构体
type bmap struct {
    tophash [8]uint8 //存储哈希值的高8位
    keys    [8]keytype  //key数组
    values  [8]valuetype // value数组
    pad     uintptr
    overflow *bmap   //溢出bucket的地址
}

在这里插入图片描述

  1. map的底层结构时 hmap
  2. 桶数组中每个桶可以存储8个元素,超过了则使用overflow链接到下一个桶(溢出桶),所以使用的是链接法

2. get操作

在这里插入图片描述

  1. 首先通过hash函数计算key的哈希值
  2. 通过后B位来定位到哪个桶
  3. 定位到桶之后,首先通过hash值高8位去便利tophash数组(起到一种加速的作用),找到对应的下标i。
  4. 然后比较keys[i]==key,如果为true,则返回values[i]
  5. 如果没有找到,则继续便利tophash数组,找到下一个符合的下标i,重复3,4两步
  6. 如果都没有找到,则去溢出桶里寻找

3. tophash数组的作用

  • 主要起到加速的作用
  • tophash数组是值数组,每个元素物理位置连续
  • 而key数组不一定是值数组,如果key是字符串,则key数组里的元素其实存的不是字符串内容本身,而是一个引用。我们需要再根据这个引用(指针)找到字符串去比较。
  • 所以如果只根据key数组去比较的话,则这个过程中访问的内存地址其实不是连续的,速度会慢很多。

4. put操作

在这里插入图片描述

  • 先根据key进行查找(过程跟get差不多),如果找到了,则替换value
  • 如果没找到,定位到对应的桶,找到一个空闲的位置i,进行插入(三个数组对应下标i处都要插入)
  • 如果对应桶没有空闲位置,则通过overflow插入到溢出桶中
  • 必要时可能会引发扩容

5. 等量扩容

在这里插入图片描述

  • 当不停地在map中put和delete操作,导致一个桶的溢出桶链很长,但是每个桶里面key存的断断续续也不是很多的时候,会出发等量扩容。
  • 所谓等量扩容本不是真的扩容了,而是碎片整理,将key都整理到一起去
  • 这种情况下元素会发生重排,但不会产生新的桶(正常桶和溢出桶)

6. 二倍扩容

在这里插入图片描述

  • 正常桶的桶数组大小会翻倍
  • 存储的元素会重排,元素所在的桶数组下标可能会改变
  • 比如扩容前B=2,扩容后B=3,之前根据key的hash后两位定位,现在看hash的后三位定位。所以对于一个key它新的同下标要么还是原来的 i,要么就是i+len(原来桶数组)

7. 扩容条件

  • 当装载因子大于6.5时扩容(ladFactor=count/(2^B)
  • 当溢出桶过多时会扩容
    • B<15时,溢出桶数量超过2^B
    • B>=15时,溢出同数量超过2^15

8. 在函数中,也是值传递

  • 跟切片一样,在函数中也是值传递
  • 但是由于底层共享存储结构,所以函数中对map内容修改,出了函数仍是可见

9. map只能与nil比较

package main

import "fmt"

func main() {
	a := make(map[string]int, 0)
	// false
	fmt.Println(a == nil)

	var b map[string]int
	// true
	fmt.Println(b == nil)
}

  • 且只有当map底层数据结构未初始化的时候map==nil才为true

参考:https://juejin.cn/post/7029679896183963678#heading-1

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/736253.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

电商平台怎么搭建

越来越多商家致力于搭建并运营自己的私域电商平台&#xff0c;大家都清楚了解拥有自己电商平台的好处。有利于品牌的塑造与提升&#xff0c;提高品牌曝光度和认知度&#xff0c;提高客户黏性&#xff0c;降低渠道成本。 乔拓云平台模板式搭建电商平台&#xff0c;方法简单实用…

【Linux实验】I/O接口实验(Vmware虚拟机、S5P6818开发板)

这里写目录标题 一、实验目的二、实验内容三、实验设备四、实验步骤五、总结 一、实验目的 掌握S5P6818芯片的I/O口控制寄存器的配置。掌握实验掌握ARM芯片使用I/O口控制LED显示。熟练使用嵌入式交叉编译器。掌握Makefile文件书写。 二、实验内容 编写程序控制实验平台的发光…

ISCSI网络存储服务

ISCSI网络存储服务 应用场景&#xff1a; 服务器硬盘空间不足&#xff0c;可能导致服务器宕机。解决方案通常有两个&#xff0c;一是拷贝出服务器中的部分数据&#xff0c;空出存储空间&#xff0c;但在生产环境中&#xff0c;数据一般会很大&#xff0c;拷贝时间会很长&…

Kubernetes 的内部架构和工作机制

Kubernetes 是一个生产级别的容器编排平台和集群管理系统&#xff0c;能够创建、调度容器&#xff0c;监控、管理服务器。 操作系统的一个重要功能就是抽象&#xff0c;从繁琐的底层事务中抽象出一些简洁的概念&#xff0c;然后基于这些概念去管理系统资源。 Kubernetes 也是…

时间序列预测 | Matlab灰狼算法(GWO)优化极限梯度提升树XGBoost时间序列预测,GWO-XGBoost时间序列预测模型,单列数据输入模型

文章目录 效果一览文章概述部分源码参考资料效果一览 文章概述 时间序列预测 | Matlab灰狼算法(GWO)优化极限梯度提升树XGBoost时间序列预测,GWO-XGBoost时间序列预测模型,单列数据输入模型 评价指标包括:MAE、RMSE和R2等,代码质量极高,方便学习和替换数据。要求2018版本及…

AD为什么使用不了Keepout层来画板框(技巧分享)

AD为什么使用不了Keepout层 背景&#xff1a;keepout层作为板框层&#xff0c;是以前AD10的老版本延续下来的习惯&#xff0c;在新版本上需要单独放置&#xff01; 在嘉立创平台上&#xff0c;习惯了用一个机械1层作为板框。当使用带有添加3D封装的pcb库&#xff0c;发现上面的…

Excel的技术分享

导出Excel的技术分享 Excel前置知识 首先大家就是在大学的计算机导论等课程肯定有了解过office全家桶中的工具之一Excel。在印象当中就是Excel是普遍使用的就是有03和07的两个不同的版本。请问一下大家就是能说一说就是这两个版本有什么区别吗&#xff1f; 显而易见就是从了直…

Elasticsearch【安装ES服务、安装kibana、Docker安装 、索引操作、文档操作】(二)-全面详解(学习总结---从入门到深化)

目录 Elasticsearch安装_安装ES服务 Elasticsearch安装_安装kibana Elasticsearch安装_Docker安装 Elasticsearch常用操作_索引操作 Elasticsearch常用操作_文档操作 Elasticsearch安装_安装ES服务 准备工作 1、 准备一台搭载有CentOS7系统的虚拟机&#xff0c;使用XSh…

(转载)支持向量机(SVM)的回归拟合(matlab实现)

与传统的神经网络相比&#xff0c;SVM具有以下几个优点&#xff1a; (1)SVM是专门针对小样本问题而提出的&#xff0c;可以在有限样本的情况下获得最优解。 (2)SVM算法最终将转化为一个二次规划问题&#xff0c;从理论上讲可以得到全局最优解&#xff0c;从而解决了传统神经网…

Python GUI编程利器:Tkinker中的布局管理器(10)

小朋友们好&#xff0c;大朋友们好&#xff01; 我是猫妹&#xff0c;一名爱上Python编程的小学生。 和猫妹学Python&#xff0c;一起趣味学编程。 今日目标 学习Tkinter的三个布局管理器&#xff1a; pack布局管理器 gird布局管理器 place布局管理器 啥是布局管理器&am…

TCP流套接字编程(模拟多个客户端与服务器交互)

目录 一、ServerSocket API 1.1、ServerSocket构造方法 1.2、ServerSocket方法 二、Socket API 2.1、socket构造方法 2.2、socket方法 三、TCP 中的长短连接 四、示例 实现聊天室功能 五、存在的问题 一、ServerSocket API ServerSocket 是创建TCP服务端Socket的…

500个线程运行串行原因排查

场景&#xff1a;项目中有业务需要开启500个线程执行&#xff0c;需要证明有500个线程在执行。用的是一台128核的电脑。服务用docker启动的。所以理论上应该是要有128个线程并行执行的。 目录 一.证明有500个线程在执行(会发现并行度很低) 1.用top命令监控进程内的线程运行情…

netty学习(5):netty实现注册中心和发送JSON数据到指定的客户端

1. 实现&#xff1a;在netty客户端实现netty客户端注册功能&#xff0c;netty客户端需要发送注册消息到netty服务端。 2. 在父工程创建Message类&#xff0c;定义消息格式和消息类型 定义消息类型&#xff1a; package message;public enum MessageType {RegisterRequest,Re…

函数重载与函数递归

一、函数重载 定义&#xff1a;两个函数的函数名称相同&#xff0c;但是参数的个数或者类型不同 参考以下代码&#xff1a; //1.public static int add(int x,int y){return x y;}//2.与1构成重载public static int add(byte a,int b){return a b;}//3.与1构成重载public s…

文件上传漏洞总结

文件上传 文件上传漏洞产生的原理 文件上传漏洞是指用户通过界面上的上传功能上传了一个可执行的脚本文件&#xff0c;而WEB端的系统并未对其进行检测或者检测的逻辑做的不够好。 文件上传漏洞的危害 1、由于是上传的文件&#xff0c;所以文件由用户决定&#xff0c;上传we…

交换机架构整理

网口的基本结构 网口扫盲三:以太网芯片MAC和PHY的关系 问:如何实现单片以太网微控制器? 问:以太网MAC是什么? 问:什么是MII? 问:以太网PHY是什么? 问:造成以太网MAC和PHY单片整合难度高的原因是什么? 问: 网卡上除RJ-45接口外,还需要其它元件吗? 问:10BaseT和100BaseTX…

LeetCode[面试题17.14]最小的K个数

难度&#xff1a;中等 题目&#xff1a; 设计一个算法&#xff0c;找出数组中最小的k个数。以任意顺序返回这k个数均可。 示例&#xff1a; 输入&#xff1a; arr [1,3,5,7,2,4,6,8], k 4 输出&#xff1a; [1,2,3,4]提示&#xff1a; 0 < len(arr) < 1000000 <…

Java设计模式之创建型-建造者模式(UML类图+案例分析)

目录 一、基本概念 二、UML类图 三、角色设计 四、案例分析 五、总结 一、基本概念 建造者模式是一种创建型设计模式&#xff0c;它使我们将一个复杂对象的构建步骤分离出来&#xff0c;使得同样的构建过程可以创建不同的表示。该模式的目的是将构建复杂对象的过程抽象化…

JavaScrpt_13 Web API 正则表达式

JavaScrpt_13 Web API 正则表达式 一、 正则表达式1. 正则基本使用2. 元字符边界符量词范围字符类 3. 替换和修饰符4. change 事件5. 判断是否有类 一、 正则表达式 正则表达式&#xff08;Regular Expression&#xff09;是一种字符串匹配的模式&#xff08;规则&#xff09;…

12_Linux异步通知

目录 异步通知简介 驱动中的信号处理 应用程序对异步通知的处理 驱动程序编写 编写测试APP 运行测试 异步通知简介 在使用阻塞或者非阻塞的方式来读取驱动中按键值都是应用程序主动读取的,对于非阻塞方式来说还需要应用程序通过poll函数不断的轮询。最好的方式就是驱动…