问题描述
我可以在 C# net 5.0 中使用哪个对象将以下信息存储在内存中以获得最快的数据检索?
输入:一个数字(长)
示例简化查找数据:
1 -> 10 : ResultA
300 -> 300 : ResultB
500 -> 10000 : ResultC
235015 -> 235820 : ResultD
...
列表还在继续(大约 300 万行查找数据)
在上面的示例数据中:
Input -> output
5 -> ResultA
300 -> ResultB
400 -> Not found/null
9999 -> ResultC
1000000 -> Not found/null
解决方法
正如 Llama 提到的,正确的方法是使用二分搜索。即使对于数百万个范围,这也应该提供足够的性能,因为对于合理分布的数据,它以 O(log n) 进行缩放。
如果范围不重叠,这样的事情应该可以工作:
// Assume sorted
var rangesArray = new[]
{
(1,10,"A"),(300,300,"B"),(500,10000,"C"),(235015,235820,"D")
};
var rangesList = rangesArray.ToList();
var toSearchFor = 9999;
var comparer = new KeyComparer<(int,int,string),int>(p => p.Item1);
var index = rangesList.BinarySearch((toSearchFor,""),comparer);
if (index < 0) // negative values mean a exact match could not be found,{
// Take bitwise complement to get index of the element larger than the toSearchFor
// remove one get the actual range to check
index = ~index -1;
if (index > 0 && toSearchFor < rangesList[index].Item2 )
{
Console.WriteLine($"Found Range {index}");
}
else
{
Console.WriteLine($"Not Found");
}
}
else
{
Console.WriteLine($"Found Exact Range {index}");
}
如何编写通用的 IComparer
public class KeyComparer<T,TKey> : IComparer<T> where TKey : IComparable<TKey>
{
private readonly Func<T,TKey> selector;
public KeyComparer(Func<T,TKey> selector) => this.selector = selector;
public int Compare(T x,T y) => selector(x).CompareTo(selector(y));
}
如果您有重叠的范围,您可能需要搜索所有较小的索引,或者使用一些更高级的搜索结构。
,这个方法怎么样?请注意,它仅考虑起始值,因为您说您只有间隙但没有重叠。在那种情况下,它会变得更加复杂。
public static class Program
{
static void Main(string[] args)
{
var random = new Random();
// Create a list with random items.
var source = Enumerable.Range(0,1000)
.Select(_ => new SortableObject { Start = random.Next(100000) })
.ToList();
// Sort the list by using a dedicated comparer
source.Sort(SortableObjectComparer.Default);
// Define an element to search for
var searchElement = new SortableObject { Start = 17432 };
// Get the index of the best matching item
var found = source.BinarySearch(searchElement,SortableObjectComparer.Default);
if(found < 0)
{
// No exact match,get the best candidates.
var nextElement = source[~found];
var beforeElement = source[~found - 1];
Console.WriteLine($"Element with lower start value: {beforeElement.Value}");
Console.WriteLine($"Element to search {searchElement.Value}");
Console.WriteLine($"Element with higher start value: {nextElement.Value}");
}
else
{
// Exact match
var matchingElement = source[found];
Console.WriteLine($"Element matching: {matchingElement.Value}");
Console.WriteLine($"Element to search {searchElement.Value}");
}
}
}
public class SortableObjectComparer : IComparer<SortableObject>
{
public static readonly IComparer<SortableObject> Default = new SortableObjectComparer();
public int Compare(SortableObject x,SortableObject y)
{
if (ReferenceEquals(x,y))
return 0;
if (ReferenceEquals(x,null))
return -1;
if (ReferenceEquals(y,null))
return 1;
return x.Start.CompareTo(y.Start);
}
}
public class SortableObject
{
public long Start { get; set; }
public long End { get; set; }
public string Value => $"Result {Start}";
}