使用类的谓词搜索通用列表 - 比循环更快?

时间:2010-05-01 17:17:50

标签: .net list generics predicate

假设我们有一个Class1的通用列表,通常有给定会话的约100个对象。 我想看看列表是否有特定的对象。 ASP.NET 2.0允许我这样做:

Dim objResult as Class1 = objList.Find(objSearch)

与传统的For循环相比,从性能角度来看,这种方法的速度如何?

这会随着列表长度的增加或减少而变化吗?

2 个答案:

答案 0 :(得分:7)

它与循环完全相同 - 这就是它在内部的作用。

如果您的列表已排序并且您正在寻找特定值,则可能会使用BinarySearch - 但如果您考虑它,如果您对谓词或顺序没有任何了解数据,它 来查看每个项目,直到找到匹配项。碰巧的是,Find被指定为返回列表中的 first 项...所以它只是以明显的顺序查看。

这将与列表的大小成线性关系 - 即,平均而言,搜索10倍大的列表将花费10倍的时间。当然,这取决于是否以及找到匹配的地方;如果第一个项目在每种情况下匹配,无论列表有多大,它都会在同一时间完成:)

老实说,只有100个物体,听起来不太可能成为瓶颈。

答案 1 :(得分:3)

您可以轻松查看.Net List如何使用Reflector实现Find方法:

Public Function Find(ByVal match As Predicate(Of T)) As T
    If (match Is Nothing) Then
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match)
    End If
    Dim i As Integer
    For i = 0 To Me._size - 1
        If match.Invoke(Me._items(i)) Then
            Return Me._items(i)
        End If
    Next i
    Return CType(Nothing, T)
End Function

两种实现之间的唯一区别是Find需要调用match而不是在循环中内联此逻辑。

有趣的是,这个简单的表现:

var persons = new List<Person>();
for (int i = 0; i < 100; i++)
{
    persons.Add(new Person { ID = i });
}

GC.Collect();
var sw = Stopwatch.StartNew();
for (int i = 0; i < 10000000; i++)
{
    persons.Find(person => person.ID == i % 100);

}
sw.Stop();
Console.WriteLine(sw.Elapsed);

GC.Collect();
sw = Stopwatch.StartNew();
for (int i = 0; i < 10000000; i++)
{
    for (int j = 0; j < 100; j++)
    {
        if (persons[j].ID == i % 100)
        {
            break;
        }
    }
}
sw.Stop();
Console.WriteLine(sw.Elapsed);

显示:
使用 查找查询列表所需的总时间为05.7990078 秒。
使用 循环查询列表所需的总时间为06.3551074 秒。

这个结果在几次执行中似乎是一致的。

修改 - 找到Find优势的解释:
Find工作得更快,因为它在每次迭代中直接访问底层数组。循环通过List索引器访问它,这需要每个访问索引验证:

Public Default Property Item(ByVal index As Integer) As T
    Get
        If (index >= Me._size) Then
            ThrowHelper.ThrowArgumentOutOfRangeException
        End If
        Return Me._items(index) // _items is the underlying array.
    End Get