我想将一组修改过的记录与从数据库中提取的记录列表进行比较,并从数据库中删除那些在传入数组中不存在的记录。修改后的数组来自维护数据库的客户端应用程序,此代码在WCF服务应用程序中运行,因此如果客户端从数组中删除记录,则应从数据库中删除该记录。以下是示例代码段:
public void UpdateRecords(Record[] recs)
{
// look for deleted records
foreach (Record rec in UnitOfWork.Records.ToList())
{
var copy = rec;
if (!recs.Contains(rec)) // use this one?
if (0 == recs.Count(p => p.Id == copy.Id)) // or this one?
{
// if not in the new collection, remove from database
Record deleted = UnitOfWork.Records.Single(p => p.Id == copy.Id);
UnitOfWork.Remove(deleted);
}
}
// rest of method code deleted
}
我的问题:在Contains方法上使用Count方法有速度优势(或其他优势)吗? Id属性保证是唯一的并且用于标识该特定记录,因此您不需要进行按位比较,因为我假设Contains可能会这样做。
任何? 谢谢,戴夫
答案 0 :(得分:35)
这会更快:
if (!recs.Any(p => p.Id == copy.Id))
这与使用Count()
具有相同的优势 - 但在找到第一个匹配后停止与Count()
不同
答案 1 :(得分:13)
您甚至不应该考虑Count
,因为您只是检查是否存在记录。您应该使用Any
代替。
使用Count
强制迭代整个可枚举以获得正确的计数,Any
一旦找到第一个元素就会停止枚举。
至于Contains
的使用,您需要考虑指定的类型引用是否等同于您正在执行的Id
比较。默认情况下不是。
答案 2 :(得分:8)
假设Record
正确实现GetHashCode
和Equals
,我会完全采用不同的方法:
// I'm assuming it's appropriate to pull down all the records from the database
// to start with, as you're already doing it.
foreach (Record recordToDelete in UnitOfWork.Records.ToList().Except(recs))
{
UnitOfWork.Remove(recordToDelete);
}
基本上没有必要有N * M的查找时间 - 上面的代码最终会根据他们的哈希码从recs
构建一组记录,并且比原始代码更有效地找到非匹配代码。
如果您真的有更多事情要做,可以使用:
HashSet<Record> recordSet = new HashSet<Record>(recs);
foreach (Record recordFromDb in UnitOfWork.Records.ToList())
{
if (!recordSet.Contains(recordFromDb))
{
UnitOfWork.Remove(recordFromDb);
}
else
{
// Do other stuff
}
}
(我不太确定为什么原始代码使用Single
从数据库中重新获取记录时已经将其作为rec
...}
答案 3 :(得分:6)
Contains()
将对您的对象使用Equals()
。如果您没有覆盖此方法,甚至可能Contains()
返回不正确的结果。如果您已覆盖它以使用对象的Id
来确定身份,那么在这种情况下Count()
和Contains()
几乎完全相同。 Contains()
除了匹配之外会立即短路,Count()
将继续计数。 Any()
可能是比两者都更好的选择。
您是否确定这是您应用中的瓶颈?感觉就像我过早优化一样。你知道,这是所有邪恶的根源。)
答案 4 :(得分:2)
因为你保证会有1只且只有1,所以Any可能会更快。因为只要找到匹配的记录就会返回true。
Count将遍历计算每次出现的整个列表。因此,如果项目在1000个项目的列表中是#1,那么它将检查1000个项目中的每个项目。
修改强>
此外,这可能是提及不进行过早优化的时候。
连接两个方法,在每个方法之前和之后放一个秒表。 创建一个足够大的列表(1000个项目或更多,具体取决于您的域。)并查看哪个更快。
我的猜测是我们在这里谈论ms的顺序。
我全都是为了编写高效的代码,只是确保你没有花费数小时来节省每天两次调用的方法5毫秒。
答案 5 :(得分:2)
就是这样:
UnitOfWork.Records.RemoveAll(r => !recs.Any(rec => rec.Id == r.Id));
答案 6 :(得分:1)
我可以建议一种替代方法,我认为应该更快,因为即使在第一场比赛之后,计数也会继续。
public void UpdateRecords(Record[] recs)
{
// look for deleted records
foreach (Record rec in UnitOfWork.Records.ToList())
{
var copy = rec;
if (!recs.Any(x => x.Id == copy.Id)
{
// if not in the new collection, remove from database
Record deleted = UnitOfWork.Records.Single(p => p.Id == copy.Id);
UnitOfWork.Remove(deleted);
}
}
// rest of method code deleted
}
这样你肯定会打破第一场比赛而不是继续计算。
答案 7 :(得分:1)
如果您需要知道实际的元素数,请使用Count();这是唯一的方法。如果要检查是否存在匹配记录,请使用Any()或Contains()。两者都比Count()快很多,并且两者的执行速度大致相同,但Contains将对整个对象进行相等检查,而Any()将根据对象评估lambda谓词。