我想知道为什么 SortedList<TKey, TValue> 在已经按键排序时不提供 BinarySearch。它也使用方法本身(例如IndexOf),但使用的数组是私有字段。所以我试图为此创建一个扩展方法。看看:
public static class SortedListExtensions
{
public static int BinarySearch<TKey, TValue>(this SortedList<TKey, TValue> sortedList, TKey keyToFind, IComparer<TKey> comparer = null)
{
TKey[] keyArray = sortedList.GetKeyArray();
if (comparer == null) comparer = Comparer<TKey>.Default;
int index = Array.BinarySearch<TKey>(keyArray, keyToFind, comparer);
return index;
}
public static IEnumerable<TKey> GetKeyRangeBetween<TKey, TValue>(this SortedList<TKey, TValue> sortedList, TKey low, TKey high, IComparer<TKey> comparer = null)
{
int lowIndex = sortedList.BinarySearch(low, comparer);
if (lowIndex < 0)
{
// list doesn't contain the key, find nearest behind
// If not found, BinarySearch returns the complement of the index
lowIndex = ~lowIndex;
}
int highIndex = sortedList.BinarySearch(high, comparer);
if (highIndex < 0)
{
// list doesn't contain the key, find nearest before
// If not found, BinarySearch returns the complement of the index
highIndex = ~highIndex - 1;
}
IList<TKey> keys = sortedList.Keys;
for (int i = lowIndex; i < highIndex; i++)
{
yield return keys[i];
}
}
private static TKey[] GetKeyArray<TKey, TValue>(this SortedList<TKey, TValue> sortedList)
{
// trying to resolve array with reflection because SortedList.keys is a private array
Type type = typeof(SortedList<TKey, TValue>);
FieldInfo keyField = type.GetField("keys", BindingFlags.NonPublic | BindingFlags.Instance);
if(keyField != null && keyField.GetValue(sortedList) is TKey[] keyArrayFromReflection)
{
return keyArrayFromReflection;
}
// fallback: fill a new array from the public Keys property, you might want to log this since you should change the reflection implementation
IList<TKey> keyList = sortedList.Keys;
TKey[] keyArray = new TKey[keyList.Count];
for (int i = 0; i < keyArray.Length; i++)
keyArray[i] = keyList[i];
return keyArray;
}
}
创建示例SortedList:
DateTime start = DateTime.Today.AddDays(-50);
var sortedList = new SortedList<DateTime, string>();
for(int i = 0; i < 50; i+=2)
{
var dt = start.AddDays(i);
sortedList.Add(dt, string.Format("Date #{0}: {1}", i, dt.ToShortDateString()));
}
DateTime low = start.AddDays(1); // is not in the SortedList which contains only every second day
DateTime high = start.AddDays(10);
现在您可以使用扩展方法来获取低键和高键之间的键范围:
IEnumerable<DateTime> dateRange = sortedList.GetKeyRangeBetween(low, high).ToList();
结果:
04/04/2014
04/06/2014
04/08/2014
04/10/2014
请注意,这是从头开始构建的,并未经过真正的测试。