Golang中for和for range语句的使用技巧、对比及常见的避坑

news2024/9/22 15:34:22

前言

基础语法不再赘述,写这个原因是之前的某次面试被问道了,我知道会导致问题但具体答下来不是很通顺。再回想自己开发过程中,很多地方都是使用到了for/for range,但是却从没注意过一些细节,因此专门学习一下进行记录。

对一个数组循环,for range,使用kv时候有什么要注意的吗?
这个是当时面经记录的问题。因此顺着这里开始进行学习。

在这里插入图片描述

for和for range基本语法

for的用法大概可以类比C++里面的for和while

//类似C++的for 
for i := 0; i < 10; i++ {
    fmt.Println(i)
}
//类似C++的while
j := 0
for j < 10 {
    fmt.Println(j)
    j++
}

//死循环
for {
    fmt.Println("无限循环")
}

fo range用法大概可以类比python的range。但是golang中他的用法更多,大致四种

//遍历数组:
numbers := [3]int{1, 2, 3}
for index, value := range numbers {
    fmt.Printf("索引:%d 值:%d\n", index, value)
}
//遍历切片:
names := []string{"Alice", "Bob", "Charlie"}
for index, name := range names {
    fmt.Printf("索引:%d 值:%s\n", index, name)
}
//遍历 map:
ages := map[string]int{"Alice": 25, "Bob": 30, "Charlie": 35}
for name, age := range ages {
    fmt.Printf("%s 的年龄是 %d 岁\n", name, age)
}
//遍历字符串:
sentence := "Hello, 世界"
for index, char := range sentence {
    fmt.Printf("索引:%d 字符:%c\n", index, char)
}

for和for range的区别

对于一个数组,例如a[3]={3.2.1},可以通过两种方式进行遍历:


func main() {
	fmt.Println("下面是for range遍历")

	var m = []int{1, 2, 3}
	for k, v := range m {
		fmt.Println(k, v)
		fmt.Println(&k, &v)
	}
	fmt.Println("下面是for遍历")
	for i := 0; i < 3; i++ {
		fmt.Println(i, m[i])
		fmt.Println(&i, &m[i])
	}
}

其结果如下:

在这里插入图片描述

我们可以发现,输出的结果是一样的,但地址却不一样:
因此这便是二者的本质区别——
1、range遍历在开始遍历数据之前,会先拷贝一份被遍历的数据。所以在遍历过程中去修改被遍历的数据,只是修改拷贝的数据,不会影响到原数据。
2、range在遍历值类型时,其中的v是一个局部变量,只会声明初始化一次,之后每次循环时重新赋值覆盖前面的。

常见的坑

通过指针取值

//此代码来自`https://zhuanlan.zhihu.com/p/105435646`
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

这个是由于上述的区别2——v是一个只声明一次的局部变量。
修改方案两种:1、通过arr[k]获取值,而不是v。
2、使用一个局部变量:tmp :=v,之后对tmp处理。

循环中添加元素是否会导致死循环

func main() {
 s := []int{0, 1}
 for _, v := range s {
  s = append(s, v)
 }
 fmt.Printf("s=%v\n", s)
}

大致意思是这样会不会死循环?
这个是上面的性质1。range因为是对复制的数据在操作,所以不会影响。普通的for则会影响。

https://cloud.tencent.com/developer/article/1925475
此部分具体参考此例即可。

对大数据的操作

对于很大数据的数组,若采取range遍历,因为复制的缘故,所以会导致开销巨大。(具体见下的性能分析)
但是在大数据的数组下,使用range去重置(全部赋值为0),效率是高的。原因是内存是连续的,编译器会直接清空这一片的内存。具体讲解见此

demo和源码看这里的3和4

对于map的操作

具体来说,就是边遍历边新增/删除。删除了的不可能再遍历到,新增的可能再遍历到(不一定)。

原因:map内部实现是一个链式hash表,为了保证无顺序,初始化时会随机一个遍历开始的位置,所以新增的元素被遍历到就变的不确定了,同样删除也是一个道理,但是删除元素后边就不会出现,所以一定不会被遍历到。

具体参考此文的最后部分

range中开goroutine

func main() {
	
	var m = []int{1, 2, 3}
	for i := range m {
		go func() {
			fmt.Print(i)
		}()
	}
	time.Sleep(time.Millisecond)

}

会发现输出结果有随机性:发现结果是222。原因是上述的那个,k、v是同一值,他并没有保存到goroutine的栈中。
解决方法还是局部变量,但可能出现012、210、102这样的值…原因是运行太快了,所以顺序不一定…

附1:range遍历的性能分析

for range需要进行一步复制操作,因此显然需要比for更多的性能开销。但是对于切片来说,因为切片的副本和原切片都是指向数组的地址,所以性能一样。
具体见此文。

https://blog.csdn.net/EDDYCJY/article/details/124701572

附2:一个坑中坑——map的range遍历并不是随机

具体参考此文

结论是:第一位的元素会有更高的概率被首先选中。因此不可用map遍历的随机性,作为随机选择map中元素的方法。

参考资源

Go 处理大数组:使用 for range 还是 for 循环?
Go的循环遍历使用小坑
Go 中for range的一个坑
Go语言中for-range使用踩坑指南
Dig101 - Go之for-range排坑指南
Golang 语言 for 和 for-range 的区别
5.1 for 和 range #

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

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

相关文章

手把手教你使用cubemx配置串口

重写fput函数 #include <stdio.h>#ifdef __GNUC__#define PUTCHAR_PROTOTYPE int _io_putchar(int ch)#else#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)#endif /* __GNUC__*//*******************************************************************brief R…

Kubernetes (八) 金丝雀发布

一. 金丝雀发布作用&#xff1a; 金丝雀发布是指在生产环境中逐步推出新版本应用程序&#xff0c;只在一小部分用户或流量中使用该版本&#xff0c;并根据反馈逐步扩…

扩展欧几里得算法总结

知识概览 裴蜀定理&#xff1a;对于任意正整数a&#xff0c;b&#xff0c;一定存在非零整数x&#xff0c;y&#xff0c;使得 而且(a, b)是a和b能凑出来的最小的正整数。 通过扩展欧几里得算法可以求得裴蜀定理中x和y的值&#xff0c;x和y的通解为 &#xff0c; 例题展示 扩展欧…

系列十三、集合

一、集合 1.1、概述 集合与数组类似&#xff0c;只不过集合中的数据量可以动态的变化。 1.2、体系图 1.3、List集合 1.3.1、特点 存放的数据可以重复且有序。 1.3.2、常见操作 /*** List集合常见操作* */ Test public void listOperateTest() {List<String> cityList …

vue3 +TS 安装使用pinia状态管理

目录 一.安装 1.下载安装依赖 2.创建src/stores/index.ts文件 3.创建src/stores/states.ts文件 4.创建src/stores/interface/index.ts文件 5.修改main.ts 6.目录结构如下 7.测试使用 8.去到首页点击按钮&#xff0c;打开控制台查看 一.安装 1.下载安装依赖 npm insta…

逆向数据宽度与符号

正数与负数的 刚好是半 所以 0 表示正数 1表示负数 有符号与无符号是程序员定的 并不是开头是1就表示负数 0表示正数 得看有无符号 双字 32bit 4字节

批量置入视频封面:一分钟教程,简单易学

在视频制作过程中&#xff0c;为视频添加引人注目的封面是吸引观众的关键。而当我们需要批量处理多个视频时&#xff0c;如何快速、准确地置入封面就显得尤为重要。本文将为您揭示这一高效技巧&#xff0c;让您在一分钟内学会批量置入视频封面&#xff0c;提升视频的吸引力与观…

C++学习笔记(二十七):c++ 动态数组vector及优化

c的动态数组vector是STL的内容&#xff0c;关于STL&#xff0c;有兴趣可自行网上搜索资料。本节主要介绍vector的基本内容以及vector的简单优化。vector当超过数组最大范围&#xff0c;需要往里面添加新的元素时&#xff0c;会在内存中创建一个比上一个更大的数组&#xff0c;将…

RT-Thread:ADC 框架应用,通过 STM32CubeMX 配置 STM32 ADC驱动

关键词&#xff1a;ADC,RT-Thread ADC,STM32 ADC应用 说明&#xff1a;本笔记是记录如何开启 RT-Thread 框架的ADC功能&#xff0c;使用系统自带的ADC函数&#xff0c;并通过 STM32CubeMX 配置 STM32 ADC驱动 。 1. 打开board.h 文件&#xff0c;找到ADC 使用配置的流程&…

CMake入门教程【核心篇】动态库与静态库的差别

😈「CSDN主页」:传送门 😈「Bilibil首页」:传送门 😈「动动你的小手」:点赞👍收藏⭐️评论📝 文章目录 1.概述2.动态库(Shared Libraries)主要特点使用场景3.静态库(Static Libraries)主要特点

java继承Thread实现多线程

1、AdminController文件 package com.controller;import com.myThread.AdminThread; import org.springframework.web.bind.annotation.*;RestController CrossOrigin RequestMapping("/admin") public class AdminController{GetMapping("/{id}")public …

离线安装jenkins:使用rpm安装包

目录 一、安装jdk1.8二、安装yum软件包三、下载rmp安装包四、安装jenkins的rpm安装包五、创建jenkins文件目录六、设置环境变量七、配置jdk位置八、配置Jenkins配置文件九、启动Jenkins十、访问Jenkins十一、安装Jenkins插件 一、安装jdk1.8 根据博客Linux操作系统安装jdk1.8并…

Rollup-plugin-bundle-analyzer VS Rollup-plugin-visualizer

分析和可视化Rollup打包后的文件的插件 Rollup-plugin-bundle-analyzerRollup-plugin-visualizer Rollup-plugin-bundle-analyzer和Rollup-plugin-visualizer都是用于分析和可视化Rollup打包后的文件的插件&#xff0c;但它们在功能和使用方式上存在一些差异。 Rollup-plugi…

作业--day43

使用手动连接&#xff0c;将登录框中的取消按钮使用qt4版本的连接到自定义的槽函数中&#xff0c;在自定义的槽函数中调用关闭函数&#xff0c;将登录按钮使用qt5版本的连接到自定义的槽函数中&#xff0c;在槽函数中判断ui界面上输入的账号是否为"admin"&#xff0c…

外包做了5个月,技术退步一大半了。。。

先说一下自己的情况&#xff0c;本科生&#xff0c;20年通过校招进入深圳某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试…

麒麟操作系统缓存rpm包,制作离线yum源

缓存rpm包&#xff0c;以make为例 mkdir -p /data/yum yumdownloader --resolve --destdir/data/yum make制作离线yum包 yum install createrepo -y cd /data/yum createrepo .写yum配置文件/etc/yum.repos.d/local.repo [local-repo] namelocal-repo baseurlfile:///data/…

分布式锁3: zk实现分布式锁2 使用临时节点(需要自旋)

一 使用临时节点实现分布式锁 1.1 代码截图 1.2 代码如下 由于zookeeper获取链接是一个耗时过程&#xff0c;这里可以在项目启动时&#xff0c;初始化链接&#xff0c;并且只初始化一次。借助于spring特性&#xff0c;代码实现如下&#xff1a; package com.atguigu.distri…

世微 AP5127 DC-DC降压恒流IC 输入12-24 输出9V 2A 车灯方案线路图

此方案应用领域&#xff1a;电动车&#xff0c;摩托车灯照明&#xff0c; 汽车灯照明&#xff0c;手电筒&#xff0c;LED照明等 AP5127 是一款 PWM 工作模式,高效率、外 围简单、内置功率管&#xff0c;适用于 12-100V 输入的高 精度降压 LED 恒流驱动芯片。输出功率可达 25W&a…

【排序算法】二、希尔排序(C/C++)

「前言」文章内容是排序算法之希尔排序的讲解。&#xff08;所有文章已经分类好&#xff0c;放心食用&#xff09; 「归属专栏」排序算法 「主页链接」个人主页 「笔者」枫叶先生(fy) 目录 希尔排序1.1 原理1.2 代码实现&#xff08;C/C&#xff09;1.3 特性总结 希尔排序 1.1…

spring Security源码讲解-WebSecurityConfigurerAdapter

使用security我们最常见的代码&#xff1a; Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter {Overrideprotected void configure(HttpSecurity http) throws Exception {http.formLogin().permitAll();http.authorizeRequests().antMatcher…