【golang】探索for-range遍历实现原理(slice、map、channel)
for-range
for-range其实是正常for循环的一种语法糖,在go语言中可以遍历arr,slice,map和channel等数据结构,但是在一些初学者使用for-range可能会遇见很多坑,这篇文章会带你探索一下for-range中非常有趣的一些实现机制。
for-range遍历数组和slice
先来看两道题目:
- 从数组中遍历获取一个指针元素切片的集合
arr := [2]int{1, 2}
res := []*int{}
for _, v := range arr {res = append(res, &v)
}
//expect: 1 2
fmt.Println(*res[0],*res[1])
//but output: 2 2
答案:从程序中可以看出我们预期输出的为1、2,但实际上却输出了2、2的答案。
- 在for-range遍历中append切片
v := []int{1, 2, 3}
for i := range v {v = append(v, i)
}
答案:上面代码遍历是会停止的。
好了,可能有些朋友看完上面两段代码的最终结果已经开始疑惑了,接下来我们来看一下for-range对
数组和slice的处理方法。
// len_temp := len(range)
// range_temp := range
// for index_temp = 0; index_temp < len_temp; index_temp++ {
// value_temp = range_temp[index_temp]
// index = index_temp
// value = value_temp
// original body
// }
看上面这段源代码我们可以看出,for-range内部调用其实还是for循环,初始化会拷贝待遍历的列表,然后每次遍历的v
都是对上面源码value_temp
这同一个元素的赋值。
- 这就可以说明我们的
题1
中为什么输出的会是2、2,对题1
中v
取地址,最终只会拿到一个地址(实际地址:&value_temp),而对应的值就是最后遍历的那个元素所附给v
的值。
想得到预期值有两种方案:
- 使用局部变量
for _, v := range arr {//局部变量v替换了v,也可用别的局部变量名v := v res = append(res, &v)
}
- 直接索引获取原来的元素
//这种其实退化为for循环的简写
for k := range arr {res = append(res, &arr[k])
}
题2
也就很好说明了,因为遍历次数在遍历前就已经确定下来了(len_temp),所以题2
最终只会循环三次。
for-range遍历map
还是先看两道题目:
- 对map遍历时删除这个元素,下一次遍历还能遍历到吗?
var m = map[int]int{1: 1, 2: 2, 3: 3}var o sync.Once
for i := range m {o.Do(func() {for _, key := range []int{1, 2, 3} {if key != i {fmt.Printf("when iteration key %d, del key %d\n", i, key)delete(m, key)break}}})fmt.Printf("%d%d ", i, m[i])
}
答案:如果删除的元素还没有被遍历到(上边once.go
函数内保证第一次执行时删除未遍历的一个元素),那么后面就不会出现。因为我们都知道map在for-range遍历中是无序遍历的,这是因为map底层数据结构就是一个链式hash表,并且初始化的时候会随机一个遍历开始的位置,所以如果还没被遍历到的元素已经被删除了,那么后面也肯定不会再出现。
- map遍历时新增的元素能被遍历到吗?
var m = map[int]int{1:1, 2:2, 3:3}
for i, _ := range m {m[4] = 4fmt.Printf("%d%d ", i, m[i])
}
答案:输出中可能会有44
,原因是因为上一条题目中原因类似(链式hash表,随机遍历开始位置)。
好了,我们再来看一下golang中for-range对map的处理方法。
// The loop we generate:
// var hiter map_iteration_struct
// for mapiterinit(type, range, &hiter); hiter.key != nil; mapiternext(&hiter) {
// index_temp = *hiter.key
// value_temp = *hiter.val
// index = index_temp
// value = value_temp
// original body
// }
遍历map时没有指定循环次数,循环体是和slice类似的。由于map底层实现与slice不同,map底层使用hash表实现,插入数据位置是随机的,所以遍历过程中新插入的数据不能保证被遍历到。
for-range对channel遍历
for-range对channel的处理
// The loop we generate:
// for {
// index_temp, ok_temp = <-range
// if !ok_temp {
// break
// }
// index = index_temp
// original body
// }
channel遍历是依次从channel中读取数据,读取前是不知道里面有多少个元素的。如果channel中没有元素,则会阻塞等待,如果channel已被关闭,则会解除阻塞并退出循环。
注意:
- 上述注释中index_temp实际上描述是有误的,应该为value_temp,因为index对于channel是没有意义的。
- 使用for-range遍历channel时只能获取到一个返回值。
相关文章:
【golang】探索for-range遍历实现原理(slice、map、channel)
for-range for-range其实是正常for循环的一种语法糖,在go语言中可以遍历arr,slice,map和channel等数据结构,但是在一些初学者使用for-range可能会遇见很多坑,这篇文章会带你探索一下for-range中非常有趣的一些实现机制…...

MATLAB | 官方举办的动图绘制大赛 | 第一周赛情回顾
嘿真的又是很久没见了,最近确实有点非常很特别小忙,今天带来一下MATHWORKS官方举办的迷你黑客大赛第三期(MATLAB Flipbook Mini Hack)的最新进展!!目前比赛已经刚好进行了一周,前两届都要求提交280个字符内的代码来生成…...

适配器模式 rust和java的实现
文章目录 适配器模式介绍何时使用应用实例优点缺点使用场景 实现java实现rust 实现 rust代码仓库 适配器模式 适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能…...

竞赛 题目:垃圾邮件(短信)分类 算法实现 机器学习 深度学习 开题
文章目录 1 前言2 垃圾短信/邮件 分类算法 原理2.1 常用的分类器 - 贝叶斯分类器 3 数据集介绍4 数据预处理5 特征提取6 训练分类器7 综合测试结果8 其他模型方法9 最后 1 前言 🔥 优质竞赛项目系列,今天要分享的是 基于机器学习的垃圾邮件分类 该项目…...

wpf devexpress项目中添加GridControl绑定数据
本教程讲解了如何添加GridControl到wpf项目中并且绑定数据 原文地址Lesson 1 - Add a GridControl to a Project and Bind it to Data | WPF Controls | DevExpress Documentation 1、使用 DevExpress Template Gallery创建一个新的空白mvvm应用程序,这个项目包括了…...

2023亚太杯数学建模A题思路解析
文章目录 0 赛题思路1 竞赛信息2 竞赛时间3 建模常见问题类型3.1 分类问题3.2 优化问题3.3 预测问题3.4 评价问题 4 建模资料5 最后 0 赛题思路 (赛题出来以后第一时间在CSDN分享) https://blog.csdn.net/dc_sinor?typeblog 1 竞赛信息 2023年第十三…...

Spark3.0中的AOE、DPP和Hint增强
1 Spark3.0 AQE Spark 在 3.0 版本推出了 AQE(Adaptive Query Execution),即自适应查询执行。AQE 是 Spark SQL 的一种动态优化机制,在运行时,每当 Shuffle Map 阶段执行完毕,AQE 都会结合这个阶段的统计信…...

算法笔记-第五章-质因子分解
算法笔记-第五章-质因子分解 小试牛刀质因子2的个数丑数 质因子分解最小最大质因子约数个数 小试牛刀 质因子2的个数 #include<cstdio> int main() {int n; scanf_s("%d", &n); int count 0; while (n % 2 0) {count; n / 2; }printf("%…...

适用于WPF的设计模式
适用于WPF的设计模式 讨论“XAML能不能写逻辑代码”这个问题。我发现这是个有歧义的问题。这个问题可以有两种意思: XAML能不能用来写逻辑代码? XAML文件里能不能包含逻辑代码? 对于第一种意思——XAML是一种声明性语言,就是用来…...
C++与多态
多态的本质是允许对象以其实际类型的行为方式来操作,而不仅仅是其静态类型所声明的方式。 多态是面向对象编程中的一种核心概念,它允许对象根据其具体类型执行相应的操作,而不是其声明的类型。我们可以使用一个经典的动物的例子来说明这一点。…...

ios 对话框UIAlertController放 tableview
//强弱引用 #define kWeakSelf(type)__weak typeof(type)weak##type type; -(void) showUIAlertTable {kWeakSelf(self)UIAlertController *alert [UIAlertController alertControllerWithTitle:NSLocalizedString("select_stu", nil) message:nil prefer…...
警告:新版Outlook会向微软发送密码、邮件和其他数据
新的免费Outlook会将敏感数据发送给 Microsoft。 在没有通知或询问的情况下,Microsoft 授予自己对新 Outlook 用户的 IMAP 和 SMTP 访问数据的完全访问权限。也就是说,当用户设置 IMAP 帐户时,新的 Outlook 会将访问数据和服务器信息发送给 …...
数据结构C语言--基础实验
实验1 线性表的顺序实现 1.!顺序表的倒置 /**********************************/ /*文件名称:lab1-01.c */ /**********************************/ /*基于sequlist.h中定义的顺序表,编写算法函数reverse(sequence_list *L)&…...

wireshark抓包并进行Eigrp网络协议分析
路由协议 Eigrp EIGRP:Enhanced Interior Gateway Routing Protocol 即 增强内部网关路由协议。也翻译为 加强型内部网关路由协议。 EIGRP是Cisco公司的私有协议(2013年已经公有化)。 EIGRP结合了链路状态和距离矢量型路由选择协议的Cisco专用协议&a…...

力扣刷题-二叉树-二叉树的层序遍历(相关题目总结)
思路 层序遍历一个二叉树。就是从左到右一层一层的去遍历二叉树。这种遍历的方式和我们之前讲过的都不太一样。 需要借用一个辅助数据结构即队列来实现,队列先进先出,符合一层一层遍历的逻辑,而用栈先进后出适合模拟深度优先遍历也就是递归的…...
fundamental notes in 3D math
平面方程 a x b y c z d axbycz d axbyczd, 法向量 a , b , c a,b,c a,b,c, 点到平面的距离为 d / s q r t ( a 2 b 2 c 2 ) d / sqrt(a^2 b^2 c^2) d/sqrt(a2b2c2) , 距离可为正, 为负, 为正表示跟法向量方向一致, 为负表示相反 点 ( x o , y o , z o ) (x_o, y_o, z…...

【Java 进阶篇】JQuery DOM操作:舞动网页的属性魔法
在前端的舞台上,属性操作是我们与HTML元素进行互动的关键步骤之一。而JQuery,这位前端开发的巫师,通过简洁而强大的语法,为我们提供了便捷的属性操作工具。在这篇博客中,我们将深入研究JQuery DOM操作中的属性操作&…...

腾讯云5年云服务器还有吗?腾讯云5年时长服务器入口在哪?
如果你是一名企业家或者是一个热衷于数字化转型的创业者,那么腾讯云最近推出的一项优惠活动绝对不会让你无动于衷。现在,腾讯云正在大力推广一项5年特价云服务器活动,只需要花费3879元,你就可以享受到腾讯云提供的优质服务。 腾讯…...
odoo在iot领域的发展情况
Odoo 是一个全面的集成商业管理软件套装,主要提供企业资源规划 (ERP)、客户关系管理 (CRM)、电子商务、会计、库存管理等功能。在 IoT(物联网)领域,Odoo 侧重于通过提供一个中心化的平台来整合多方面的业务流程,包括生…...

Spark 之 入门讲解详细版(1)
1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室(Algorithms, Machines, and People Lab)开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目,8个月后成为Apache顶级项目,速度之快足见过人之处&…...

前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...

Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...
Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!
一、引言 在数据驱动的背景下,知识图谱凭借其高效的信息组织能力,正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合,探讨知识图谱开发的实现细节,帮助读者掌握该技术栈在实际项目中的落地方法。 …...
leetcodeSQL解题:3564. 季节性销售分析
leetcodeSQL解题:3564. 季节性销售分析 题目: 表:sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...

前端开发面试题总结-JavaScript篇(一)
文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包(Closure)?闭包有什么应用场景和潜在问题?2.解释 JavaScript 的作用域链(Scope Chain) 二、原型与继承3.原型链是什么?如何实现继承&a…...
现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?
现有的 Redis 分布式锁库(如 Redisson)相比于开发者自己基于 Redis 命令(如 SETNX, EXPIRE, DEL)手动实现分布式锁,提供了巨大的便利性和健壮性。主要体现在以下几个方面: 原子性保证 (Atomicity)ÿ…...

MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...

Web后端基础(基础知识)
BS架构:Browser/Server,浏览器/服务器架构模式。客户端只需要浏览器,应用程序的逻辑和数据都存储在服务端。 优点:维护方便缺点:体验一般 CS架构:Client/Server,客户端/服务器架构模式。需要单独…...
6个月Python学习计划 Day 16 - 面向对象编程(OOP)基础
第三周 Day 3 🎯 今日目标 理解类(class)和对象(object)的关系学会定义类的属性、方法和构造函数(init)掌握对象的创建与使用初识封装、继承和多态的基本概念(预告) &a…...