C# 基于列表类型的属性对列表进行二进制搜索
我有一个C# 基于列表类型的属性对列表进行二进制搜索,c#,list,binary-search,C#,List,Binary Search,我有一个T列表,该列表是根据一些int索引ID预先排序的。我想看看是否有一个ID元素,所以二进制搜索是最快的方法 问题是我不想基于t进行比较,我想基于ID属性进行比较 由于列表非常庞大,并且它位于应用程序中性能稍有敏感的部分,因此不可能对整个列表应用一些主要的转换(例如selecting forobj.ID)。最好在二进制搜索继续工作时执行O(lgn)搜索并转换项目 我试着让lambda工作 // T = the List<T> type // I = the type for tr
T
列表,该列表是根据一些int索引ID
预先排序的。我想看看是否有一个ID元素,所以二进制搜索是最快的方法
问题是我不想基于t
进行比较,我想基于ID
属性进行比较
由于列表非常庞大,并且它位于应用程序中性能稍有敏感的部分,因此不可能对整个列表应用一些主要的转换(例如select
ing forobj.ID
)。最好在二进制搜索继续工作时执行O(lgn)
搜索并转换项目
我试着让lambda工作
// T = the List<T> type
// I = the type for transforming T -> I (as in I would be `int` in this example)
internal class BinarySearchHelper<T, I> : IComparer<I>
{
private readonly Func<T, I> _transformer;
private readonly Func<I, I, int> _comparer;
public BinarySearchHelper(Func<T, I> transformFunc, Func<I, I, int> comparerFunc)
{
_transformer = transformFunc;
_comparer = comparerFunc;
}
public int Compare(I x, I y) => _comparer(_transformer(x), _transformer(y));
}
//T=列表类型
//I=转换T->I的类型(如本例中的I为'int')
内部类BinarySearchHelper:IComparer
{
专用只读函数转换器;
专用只读函数比较器;
公共二进制搜索帮助程序(Func transformFunc、Func comparerFunc)
{
_transformer=transformFunc;
_比较器=比较器函数;
}
公共整数比较(IX,YY)=>_比较器(_transformer(x),_transformer(y));
}
但是后来我发现了List.BinarySearch()
是在列表类型T
的基础上运行的,而不是我想要的属性类型,所以这是因为我想要按ID进行搜索(这是一个int
而不是T
)
我找不到任何可以进行一般二进制搜索的独立函数,我可以使用的标准库(甚至可以通过nuget添加的公共库)中是否存在这样的函数?还是我必须自己滚?我并不介意自己滚动,因为算法很简单,但我喜欢不写其他地方可能已经做过并且已经过单元测试的东西。我不知道我是否只是在.NET生态系统中某个util类的某个地方缺少了一个(尽管是(.NET Core,不是Framework),或者我是否必须去别处看看,或者自己动手 您只需要很少的定制比较器即可满足您的需求:
public class MyComparer : Comparer<MyClass>
{
public override int Compare(MyClass x, MyClass y) => x.Id.CompareTo(y.Id);
}
使用按您喜欢的任何属性比较项目。实现IComparer比编写自己的二进制搜索要容易得多。请注意,您希望它是一个
IComparer
,因此它通过I
比较T
\u transformer
接受一个T
,但在Compare
方法中传递一个I
。@Water只需创建一个T
,其值为您要搜索的I
。@juharr实例化类型T可能比滚动我自己的二进制搜索更费事,因为所搜索的对象很复杂对于@EdPlunkett,整篇文章都是为了避免写二进制搜索。是的,我可以花大量的时间编写它,做一系列的测试(恼人的部分),或者做一些界面黑客,但这两种解决方案都需要相当数量的代码,而不是在一个单行解决方案中可以实现的代码。我相信您知道,我需要编写的代码越少,其他开发人员需要阅读的代码就越多。然而,我想要的可能不存在,对我来说正确的答案是“不,做X”,其中X是上述两种解决方案之一。我想在做X之前先知道是还是不是。
public class LambdaComparer<TSource, TKey> : IComparer<TSource>
where TKey : IComparable<TKey>
{
private readonly Func<TSource, TKey> _keySelector;
public LambdaComparer(Func<TSource, TKey> keySelector)
{
if (keySelector == null)
{
throw new ArgumentNullException(nameof(keySelector));
}
_keySelector = keySelector;
}
public override int Compare(TSource x, TSource y)
{
var xKey = _keySelector(x);
var yKey = _keySelector(y);
if (xKey == null)
{
return yKey == null ? 0 : -1;
}
return _keySelector(x).CompareTo(_keySelector(y));
}
}
public static class ListExtensions
{
public static int BinarySearch<TSource, TKey>(
this List<TSource> list,
TSource item,
Func<TSource, TKey> keySelector) where TKey : IComparable<TKey> =>
list.BinarySearch(item, new LambdaComparer<TSource, TKey>(keySelector));
}
var list = new[]
{
"AVeryLongString",
null,
"NotLong",
"A",
"ZZZ"
};
var orderedByDefault = list.OrderBy(s => s).ToList();
var orderedByLength = list.OrderBy(s => s?.Length).ToList();
var indexOfZZZByDefault = orderedByDefault.BinarySearch("ZZZ");
var indexOfZZZByLength = orderedByLength.BinarySearch(
"ZZZ",
s => s?.Length ?? 0);
Assert.Equal(4, indexOfZZZByDefault);
Assert.Equal(2, indexOfZZZByLength);