Tony Bai · Go 语言第一课
Tony Bai
资深架构师,tonybai.com 博主
20432 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 59 讲
开篇词 (1讲)
结束语 (1讲)
Tony Bai · Go 语言第一课
15
15
1.0x
00:00/00:00
登录|注册

19|控制结构:Go的for循环,仅此一种

你好,我是 Tony Bai。
上一节课,我们开始了对程序控制结构的学习,学习了分支结构中的 if 语句。上节课我们也说过,针对程序的分支结构,Go 提供了 if 和 switch-case 两种语句形式。那你肯定在想,这节课肯定是要讲 switch-case 语句了吧!我不想按常规出牌,这一节课我们换换口味,挑战一下程序控制结构中最复杂的一款:循环结构。
为什么这么设置呢?因为我想让你能更早开始动手编写具有循环结构的 Go 代码。虽然 switch-case 分支结构也非常重要,但毕竟我们已经有了 if 分支语句的基础了,很多时候用 if 也可以替代 switch-case,所以把它往后放放也没关系。
日常编码过程中,我们常常需要重复执行同一段代码,这时我们就需要循环结构来帮助我们控制程序的执行顺序。一个循环结构会执行循环体中的代码直到结尾,然后回到开头继续执行。 主流编程语言都提供了对循环结构的支持,绝大多数主流语言,包括 C 语言、C++、Java 和 Rust,甚至连动态语言 Python 还提供了不止一种的循环语句,但 Go 却只有一种,也就是 for 语句。
所以这节课,我们就来系统学习一下 Go 语言循环结构中的这一支独苗,for 语句,聚焦于它的使用形式和常见坑点,让你能更快上手 Go 编码。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结
仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Tony Bai · Go 语言第一课》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(32)

  • 最新
  • 精选
  • 布凡
    老师,最后问题三:遍历 map 中元素的随机性中举的例子没看懂: 1、示例1中,当 k="tony"作为第一个迭代的元素时,我们将得到如下结果:包含了“tony”,是因为for循环中读取的是“tony”不允许被删除吗? 2、示例2中,是当m["lucky"]=24 这个值被其它原map中的值覆盖,导致赋值不成功吗? 还请老师指教

    作者回复: 1. 不是啊。之所以还能输出tony,是因为k, v从map中获取值的操作发生在delete之前啊。如果k="tony"作为第一个迭代的元素时,我们用k,v从map中取出了tony, 21。然后delete掉tony,此时k, v的值已经是tony, 21了,输出就正常了。如果tony不是第一个迭代元素,那么已经被删除掉了,后续迭代就不会输出它了。 2. 不是被覆盖了。对map迭代的实质是按顺序逐个bucket的遍历,每个bucket也是逐个遍历其中的key。如果luckey创建与第一个被遍历的元素之前了,那么后续就不会遍历它了。别忘了,key存储在哪里是根据hash值来定的。

    40
  • 用0和1改变自己
    用数组指针替换数组 func main() { var a = [5]int{1, 2, 3, 4, 5} var r [5]int fmt.Println("original a =", a) for i, v := range &a { //a 改为&a if i == 0 { a[1] = 12 a[2] = 13 } r[i] = v } fmt.Println("after for range loop, r =", r) fmt.Println("after for range loop, a =", a) }

    作者回复: 正确✅

    3
    36
  • lesserror
    Tony Bai老师,你在评论中说:“如果luckey创建与第一个被遍历的元素之前了,那么后续就不会遍历它了。别忘了,key存储在哪里是根据hash值来定的”。 这个我还是似懂非懂,能举例说明一下么? 非常感谢。

    作者回复: map的遍历顺序有随机性。但这种随机仅仅是在创建初始iterator时随机选择一个bucket。假设按bucket2->bucket3->...顺序迭代,假设已经遍历完bucket2,正在遍历bucket3,此时插入lucy这个key,恰插到bucket2中,由于之前已经遍历完bucket2,后续的遍历不会再重复遍历bucket2,于是lucy便无法出现在后续遍历路径上。如果lucy插入到bucket3后面的bucket中,则会出现在遍历路径上,我们就能看到这个key。

    5
    13
  • 罗杰
    map 中的坑比想象的要多,使用的时候一定要细心。老师基本上把能遇到的坑都指出来了。惭愧的是 continue 和 break 的 label 从来没用过。

    作者回复: 看来你日常写的业务逻辑还不够复杂。居然没有嵌套循环:)

    3
    8
  • crabxyj
    问题三:java 中是不允许在遍历中修改当前集合的,和fastfail有关,直接会抛出异常,而go允许,但遍历结果不可控

    作者回复: 👍

    7
  • 一步
    对于 map 遍历的那个例子,新增一个 map key m["lucy"] = 24 , 这里的结果counter 不应该一直是 4吗? 给 map 添加的元素为什么有的时候可以访问到 有的时候访问不到?

    作者回复: 是否访问到,视m["lucy"]=24这个键值对的插入位置而定。

    4
    4
  • 酥宝话不多
    传数组地址,&a

    作者回复: ✅

    3
  • 白小白
    老师,请教一下:最后一个例子的结果出现的原因正是因为map 中元素的随机性,如何能保证只输出一种结果?

    作者回复: 有随机性,无法保证输出哪种结果。

    归属地:辽宁
    2
  • William Ning
    老师同学好, 关于评论列表中第一条 中的 第二个问题,就是map新元素的插入,位置是随机的,不定的,所以,可能插入到原来第一个元素的前面,也可能在后面,如果在前面,就被跳过了,便没有输出。 从个人代码执行,输出结果便可知,m["lucy"] = 24 插入的位置,确实会出现在任意的位置,因为输出的位置,从0-3都有~ 但是关于上面的回答中的“,别忘了,key存储在哪里是根据hash值来定的” 如果是这样,m["lucy"] = 24,lucy应该是一个确定的值,不论经过次重复的hash,hash值应该都是一样的,也就是说,插入的位置,应该都是确定的,那么输出结果应该只有上面结果的中的一种可能,我的理解出了什么偏差吗? 谢谢老师,同学~ 下面是输出结果,供参考 ➜ golearning go run . tony 21 tom 22 jim 23 lucy 24 counter is 4 ➜ golearning go run . tony 21 tom 22 jim 23 counter is 3 ➜ golearning go run . tony 21 tom 22 jim 23 lucy 24 counter is 4 ➜ golearning go run . tony 21 tom 22 jim 23 lucy 24 counter is 4 ➜ golearning go run . tony 21 tom 22 jim 23 lucy 24 counter is 4 ➜ golearning go run . tony 21 tom 22 jim 23 lucy 24 counter is 4 ➜ golearning go run . tony 21 tom 22 jim 23 lucy 24 counter is 4 ➜ golearning go run . tom 22 jim 23 lucy 24 tony 21 counter is 4 ➜ golearning go run . tony 21 tom 22 jim 23 lucy 24 counter is 4 ➜ golearning go run . tom 22 jim 23 lucy 24 tony 21 counter is 4 ➜ golearning go run . tony 21 tom 22 jim 23 lucy 24 counter is 4 ➜ golearning go run . tony 21 tom 22 jim 23 lucy 24 counter is 4 ➜ golearning go run . tony 21 tom 22 jim 23 lucy 24 counter is 4 ➜ golearning go run . tony 21 tom 22 jim 23 lucy 24 counter is 4 ➜ golearning go run . tony 21 tom 22 jim 23 lucy 24 counter is 4 ➜ golearning go run . tom 22 jim 23 lucy 24 tony 21 counter is 4 ➜ golearning go run . jim 23 lucy 24 tony 21 tom 22 counter is 4 ➜ golearning go run . tom 22 jim 23 lucy 24 tony 21 counter is 4 ➜ golearning go run . jim 23 lucy 24 tony 21 tom 22 counter is 4

    作者回复: 虽然插入的位置可能是固定的,但遍历的起点是随机的。看看你的程序输出的的结果 是不是按tony , tom , jim, lucy组成了一个环。

    2
    2
  • pyhhou
    关于思考题,除了换成切片或者指针外,我们可以将 for range 替换为传统的 for ;; 循环就可以解决问题。 这里有一个衍生的问题,还烦请老师解答,如果说 for range 会对遍历的结构产生副本,那么我们用 for range 去遍历大型的数组的话是不是会有性能或者资源浪费等问题?所以说在平时,我们还是尽量用传统的三段式 for 循环而不是 for range?这样即使是不太了解 go 的人来看代码也不会有困惑

    作者回复: 好问题!我是这么想的: 第一尽量用切片代替纯数组loop吧,这样可以获得稳定且一致的性能。 第二,即便纯数组loop,for range也不一定比经典for loop性能差,go语言对for range做了特殊优化。不过也要看for range的数组的元素类型,如果是包含一定数量字段的结构体类型,目前优化还不到位。

    2
收起评论
显示
设置
留言
32
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部