天坑,这样一个lambda随机取数据也有Bug
前几天,一位网友跟我说他编写的一段很简单的代码遇到了奇怪的Bug,他要达到的效果是从一个List中随机取出来一条数据,代码如下:
1 var random = new Random();
2 var users = Enumerable.Range(0, 10).Select(p => new User(p, "A" + p)).ToList();
3 var user = users.Find(p => p.Id == random.Next(0, 10));
4 Debug.Assert(user != null);
5
6 record User(int Id,string Name);
第2行代码生成了一个包含10个User对象的List,这些User的Id值从0递增到9;第3行代码中调用List的Find方法来根据lambda表达式来查找一条数据,这里通过random.Next()来获取一个[0,10)之间的随机数,然后用这个随机数来和Id进行比较。按照逻辑来讲,Find一定可以找到一条数据,所以在第4行代码中断言user一定不为null。但是这段代码有的时候运行正常,有的时候则会断言失败,从而程序抛出异常,令人不解。
当然,他的这段代码写的过于复杂,其实改成users[random.Next(0, 10)]就简单又高效。但是为了揭示问题的本质,我这里继续分析为什么用Find+lambda方法会出现问题。
我们查看一下Find方法的源代码,如下:
public T? Find(Predicate<T> match)
{
for (int i = 0; i < _size; i++)
{
if (match(_items[i]))//注意这里
{
return _items[i];
}
}
return default;
}
Find方法的逻辑很简单,就是遍历List中的数据,对于每条数据都调用match这个委托来判断当前这条数据是否满足条件,如果找到一条满足条件的数据,就把它返回。如果走到最后都没有找到,就返回默认值(比如null)。这个逻辑简单到貌似看不到任何问题。
问题的关键就在if (match(_items[i]))这一句代码。它是在每一次循环都调用一下match的委托来判断当前数据的匹配性。而match指向的委托的方法体是p => p.Id == random.Next(0, 10),也就是每次匹配判断都要获取一个新的随机数来进行比较。假设在循环的时候生成的10个随机数为:9,8,8,7,9,1,1,2,3,4,那么就会每次match(_items[i])判断的结果都为false,从而导致最后返回null,也就是找不到任何的数据。
明白了原理之后,解决这个问题的思路就是不要在lambda中生成待比较的随机数,而是提前生成随机数,代码如下:
int randId = random.Next(0, 10);
var user = users.Find(p => p.Id == randId);
同样的原理也适用于Single()、Where()等LINQ操作。在这些操作中也要避免在lambda表达式中再进行复杂的计算,这样不仅可以避免类似这篇文章中提到的bug,而且可以提升程序的运行效率。
欢迎阅读我编写的《ASP.NET Core技术内幕与项目实战》,这本书的宗旨就是“讲微软文档中没有的内容,讲原理、讲实践、讲架构”。具体见右边公告。
天坑,这样一个lambda随机取数据也有Bug的更多相关文章
- MySQL随机取数据
// 随机取9个 $rand_sql = "SELECT * FROM `tf_product` WHERE (`id` >= ((SELECT MAX(`id`) FROM `tf_ ...
- MySQL 随机取数据效率问题
本文详细解说了MySQL Order By Rand()效率优化的方案,并给出了优化的思路过程,是篇不可多得的MySQL Order By Rand()效率美文. 最近由于需要大概研究了一下MYSQL ...
- SQL 在表中随机取数据
在一张10万行产品表(Product)中,随机取10条数据的几种方式: SET STATISTICS IO ON SELECT TOP 10 ID FROM dbo.Product(NOLOCK) W ...
- mysql实现高效率随机取数据
从数据库中(mysql)随机获取几条数据很简单,但是如果一个表的数据基数很大,比如一千万,从一千万中随机产生10条数据,那就相当慢了,如果同时一百个人访问网站,处理这些个进程,对于一般的服务器来说,肯 ...
- Sql Server随机取数据
select top 10 * from tablename order by NEWID()
- oracle随机取数据
select * from (select rownum,KEYWORD, CATEGORY,CREATE_DATE,UPDATE_DATE from (select * from knet_keyw ...
- mysql 随机取数据
SELECT * FROM table WHERE id >= (SELECT FLOOR(RAND()*MAX(id)) FROM table ) ORDER BY idLIMIT 1; 这样 ...
- MySQL随机获取数据的方法,支持大数据量
最近做项目,需要做一个从mysql数据库中随机取几条数据出来. 总所周知,order by rand 会死人的..因为本人对大数据量方面的只是了解的很少,无解,去找百度老师..搜索结果千篇一律.特发到 ...
- 【MySQL】随机获取数据的方法,支持大数据量
在mysql中带了随机取数据的函数,在mysql中我们会有rand()函数,很多朋友都会直接使用,如果几百条数据肯定没事,如果几万或百万时你会发现,直接使用是错误的.下面我来介绍随机取数据一些优化方法 ...
- Oracle的trunc和dbms_random.value随机取n条数据
今天在review项目代码的时候看到这样一个问题,有一张号码表,每次需要从这样表中随机取6个空闲的号码,也就是每次取出来的6个号码应该都会有所不同.然后我就看到了这样的SQL select t.* ...
随机推荐
- k8s集群中安装rook-ceph
容器的持久化存储 容器的持久化存储是保存容器存储状态的重要手段,存储插件会在容器里挂载一个基于网络或者其他机制的远程数据卷,使得在容器里创建的文件,实际上是保存在远程存储服务器上,或者以分布式的方式保 ...
- Docker 部署 Confluence(破解版)
一. 说明 1.1 素材 本文采用素材如下: Docker镜像 Github链接(https://github.com/cptactionhank) 破解工具 Gitee链接(https://gite ...
- aardio + Python 可视化快速开发桌面程序,一键生成独立 EXE
网络上大家分享的 aardio + Python 混合开发的文章很多,不得不说 aardio 与 Python 混合开发是真的简单 ! 快速入门 推荐几个快速上手教程:< aardio + P ...
- 如何在服务器上部署WebDeploy
之前项目中网站发布都是手工拷贝文件,特别麻烦,看到好多用webdeploy一键部署网站到IIS服务器,我也学习了一下. 第一步,打开服务器管理器 打开方式是开始菜单=>管理工具=>服务器管 ...
- SpringMvc(五) - 支付宝沙箱和关键字过滤,md5加密,SSM项目重要知识点
1.支付宝沙箱 1.1 jar包 alipay-sdk <!-- alipay-sdk --> <dependency> <groupId>com.alipay.s ...
- Dropout----Dropout来源
目录 一.简单介绍及公式 二.为什么dropout有效-原因定性分析 2.1 ensemble论 2.1.1 ensemble 2.1.2 动机:联合适应(co-adapting) 思考: 2.1.3 ...
- iptables使用详解
iptables使用详解 @(linux)[iptables] 前言 最近买了一个VPS,并在上面搭了DOCKER,然后再DOCKER中安装Mysql.但只要将网络端口映射到宿主机上,那么外部网络就可 ...
- python-D2-计算机与编程语言
计算机五大核心 控制器 计算机的指挥系统,可以控制计算机硬件的整体运行 运算器 实现算术运算和逻辑运算 控制器和运算器结合起来就是cpu,也称为中央处理器,是整个电脑的核心. 存储器 分为两类,非永久 ...
- Failed to convert from type [java.lang.String] to type [java.util.Date] for value '2020-02-06'; nested exception is java.lang.IllegalArgumentException]解决
今天做springbook项目前端输入日期传到数据库保存报了一下错误 Whitelabel Error Page This application has no explicit mapping fo ...
- WiresShark
WireShark 分析数据包技巧 确定WireShark的位置[是否在公网上] 选择捕获接口,一般都是internet网络接口 使用捕获过滤器 使用显示过滤器[捕获后的数据包还是很复杂,用显示过滤器 ...