列表的Find(s)方法如何在C#中工作

时间:2012-03-24 18:54:45

标签: c# .net

通过列表循环的最佳方法是什么? for循环比许多List类的find方法好吗?另外如果我使用它的find methed,如下所述,它是匿名委托谓词委托的一个实例,它是否比使用lambda表达式更好?哪一个会执行得更快?

var result = Books.FindLast(
        delegate(Book bk)
        {
            DateTime year2001 = new DateTime(2001,01,01);
            return bk.Publish_date < year2001;
        });

2 个答案:

答案 0 :(得分:2)

这是一个复杂问题,因为它涉及很多不同的主题。

通常,委托比简单的函数调用慢很多倍,但枚举列表(通过foreach)也非常慢。

如果你真的关心性能(但不要事先做个人资料!)你应该避免代表和枚举。第一个重要步骤(尽可能)可以使用Hashtable而不是简单列表。

实施例

现在举一些例子,我将以不同的方式编写相同的函数,从更易读(但更慢)到可读性更低(但更快)。我省略了每个错误检查,但不应该使用真实世界的函数(至少需要一些断言)。

这个函数使用LINQ,它更容易理解,但速度最慢。 请注意,books可以是通用枚举(不一定是List<T>

public static Book FindLastBookPublishedBefore(IEnumerable<Book> books, 
                                               DateTime date)
{
 return books.FindLast(x => x.Publish_date < date);
}

与之前相同,但没有LINQ。注意这个功能 处理特殊情况:该列表不包含任何符合条件的书籍。

public static Book FindLastBookPublishedBefore(IEnumerable<Book> books,
                                               DateTime date)
{
 Book candidate = null;
 foreach (Book book in books)
 {
  if (candidate == null || candidate.Publish_date > book.Publish_date)
   candidate = book;
 }

 return candidate;
}

与之前相同但没有枚举,请注意此功能 处理特殊情况:该列表不包含任何符合条件的书籍。

public static Book FindLastBookPublishedBefore(List<Book> books,
                                               DateTime date)
{
 Book candidate = null;
 for (int i=0; i < books.Count; ++i)
 {
  if (candidate == null || candidate.Publish_date > books[i].Publish_date)
   candidate = books[i];
 }

 return candidate;
}

与之前相同,但@MaratKhasanov建议使用SortedList<T>。请注意,使用此容器,您将在搜索过程中获得良好的性能,但插入新元素可能比普通未排序列表更慢(因为列表本身必须保持排序)。如果列表中的元素数量非常高,您可以考虑使用Hashtable编写自己的排序列表(例如,使用年份作为第一级的键)。

public static Book FindLastBookPublishedBefore(SortedList<Book> books,
                                               DateTime date)
{
 Book candidate = null;
 for (int i=0; i < books.Count; ++i)
 {
  DateTime publishDate = books[i].Publish_date;

  if (publishDate > date)
   return candidate;

  if (candidate == null || candidate.Publish_date > publishDate)
   candidate = books[i];

 }

 return candidate;
}

现在的示例有点复杂但具有最佳搜索性能。算法来自普通binary search(请注意,如果要匹配与谓词匹配的第一个元素,则可以直接使用List.BinarySearch方法。) 请注意,代码未经测试且可以进行优化,请将其视为一个示例。

public static Book FindLastBookPublishedBefore(List<Book> books,
                                               DateTime date)
{
 int min = 0, max = books.Count;
 Book candidate = null;

 while (min < max)
 {
  int mid = (min + max) / 2;
  Book book = books[mid];

  if (book.Publish_date > date)
   max = mid - 1;
  else
  {
   candidate = book;
   ++min;
  }

  if (min >= max)
   break;
 }

  return candidate;
}

在转移到更复杂的容器之前,您可能会考虑在第一次搜索之前保持SortedList<T>未排序。它会非常慢(因为它也会对列表进行排序)但插入速度与普通列表一样快(但您必须尝试使用​​真实世界数据)。无论如何,最后的算法可以进行很多优化。

也许如果您的收藏中有这么多项目无法使用普通集合管理它们,您可能会认为将所有内容都移到数据库中... lol

答案 1 :(得分:2)

使用任何使代码更具可读性的东西。您可以使用lambda表达式简化上面的代码;它们只是匿名委托的简化语法。无论您何时可以使用委托,都可以使用lambda表达式或常规方法。你可以将普通方法作为参数传递而不用括号。

C#编译器实际上只为匿名委托和lambda表达式创建了一个隐藏方法。你可能不会遇到速度上的任何差异。

var result = Books.FindLast(bk => bk.Publish_date < new DateTime(2001,01,01));