如何列出';s的Find(s)方法在C#中有效

本文关键字:方法 有效 Find 何列出 | 更新日期: 2023-09-27 18:28:50

循环列表的最佳方式是什么?for循环比许多List类的find方法好吗?此外,如果我使用下面提到的find方法,即匿名委托谓词委托的实例,这比使用lambda表达式更好吗?哪一个执行得更快?

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

如何列出';s的Find(s)方法在C#中有效

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

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

如果您真的关心性能(但不要事先这样做,概要!),则应该避免委托和枚举。第一个重要步骤(只要可能)可以是使用哈希表,而不是简单的列表。

示例

现在举几个例子,我将用不同的方式编写相同的函数,从可读性更强(但速度较慢)到可读性较差(但速度较快)。我省略了每一个错误检查,但现实世界中的函数不应该这样做(至少需要一些断言)。

这个函数使用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;
}

现在是一个稍微复杂一点的示例,但具有最佳搜索性能。算法是从一个普通的二进制搜索中派生出来的(注意,如果你想匹配与谓词匹配的第一个元素,你可以直接使用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

使用任何使代码更可读的东西。您可以通过使用lambda表达式来简化上面的代码;它们只是匿名委托的简化语法。只要可以使用委托,就可以使用lambda表达式或普通方法。您可以将一个普通方法作为参数传递,而不使用括号。

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

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