新编辑:我将在下面留下额外的二进制搜索,因为它们对其他人有用,这是我认为您真正想要的最后一个选项。如果它正在寻找的项目“小于”指定的项目,您的代理应该返回一个正数,如果它“大于”指定的项目,则返回一个负数,如果它是正确的,则返回 0。
public static int BinarySearchForMatch<T>(this IList<T> list,
Func<T,int> comparer)
{
int min = 0;
int max = list.Count-1;
while (min <= max)
{
int mid = (min + max) / 2;
int comparison = comparer(list[mid]);
if (comparison == 0)
{
return mid;
}
if (comparison < 0)
{
min = mid+1;
}
else
{
max = mid-1;
}
}
return ~min;
}
旧编辑:我将在下面留下原来的答案,但这里有两个其他选项。
第一个从源数据投影到键类型,并指定要查找的键。比较本身只是以该键类型的默认方式完成:
public static int BinarySearchBy<TSource,TKey>(this IList<TSource> list,
Func<TSource,TKey> projection, TKey key)
{
int min = 0;
int max = list.Count-1;
while (min <= max)
{
int mid = (min + max) / 2;
TKey midKey = projection(list[mid]);
int comparison = Comparer<TKey>.Default.Compare(midKey, key);
if (comparison == 0)
{
return mid;
}
if (comparison < 0)
{
min = mid+1;
}
else
{
max = mid-1;
}
}
return ~min;
}
第二个采用 Func 代替,将列表中的项目与我们正在寻找的键进行比较。当然,代码几乎完全相同 - 只是比较发生了变化:
public static int BinarySearchBy<TSource,TKey>(this IList<TSource> list,
Func<TSource,TKey,int> comparer, TKey key)
{
int min = 0;
int max = list.Count-1;
while (min <= max)
{
int mid = (min + max) / 2;
int comparison = comparer(list[mid], key);
if (comparison == 0)
{
return mid;
}
if (comparison < 0)
{
min = mid+1;
}
else
{
max = mid-1;
}
}
return ~min;
}
这些都未经测试,但至少可以编译:)
原答案:
您可以将List<T>.BinarySearch 与IComparer<T> 一起使用。您不必编写自己的IComparer<T> 实现——我已经在MiscUtil 中继续,它从Comparison<T> 委托构建IComparer<T>。这只会发现前三个条件,但二分查找会从其余条件中找出最后一个。
事实上,代码太短了,我不妨把它贴在这里(诚然,没有 cmets)。
public sealed class ComparisonComparer<T> : IComparer<T>
{
readonly Comparison<T> comparison;
public ComparisonComparer(Comparison<T> comparison)
{
if (comparison == null)
{
throw new ArgumentNullException("comparison");
}
this.comparison = comparison;
}
public int Compare(T x, T y)
{
return comparison(x, y);
}
}
所以你可能会这样做:
var comparer = new ComparisonComparer<Person>((p1, p2) => p1.ID.CompareTo(p2.ID));
int index = list.BinarySearch(employee, comparer);
MiscUtil 还有一个您可能感兴趣的 ProjectionComparer - 您只需指定一个投影,就像在带有 LINQ 的 OrderBy 中一样 - 但它实际上取决于您的用例。