哪个在.NET中更快,.Contains()或.Count()?

时间:2011-03-17 15:51:30

标签: c# .net linq

我想将一组修改过的记录与从数据库中提取的记录列表进行比较,并从数据库中删除那些在传入数组中不存在的记录。修改后的数组来自维护数据库的客户端应用程序,此代码在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可能会这样做。

任何? 谢谢,戴夫

8 个答案:

答案 0 :(得分:35)

这会更快:

if (!recs.Any(p => p.Id == copy.Id)) 

这与使用Count()具有相同的优势 - 但在找到第一个匹配后停止Count()不同

答案 1 :(得分:13)

您甚至不应该考虑Count,因为您只是检查是否存在记录。您应该使用Any代替。

使用Count强制迭代整个可枚举以获得正确的计数,Any一旦找到第一个元素就会停止枚举。

至于Contains的使用,您需要考虑指定的类型引用是否等同于您正在执行的Id比较。默认情况下不是。

答案 2 :(得分:8)

假设Record正确实现GetHashCodeEquals,我会完全采用不同的方法:

// 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谓词。