C# 在SortedList中获取两个键之间的所有键的最快方法是什么?
给定一个填充的C# 在SortedList中获取两个键之间的所有键的最快方法是什么?,c#,.net,C#,.net,给定一个填充的SortedList。我想得到给定的高低日期时间间隔的所有键(或者它们的索引范围,应该是一个闭合的整数间隔(错过了什么?) 注:低值和高值实际上不需要在SortedList中 如果有人对如何在没有SortedList的情况下实现这一点有更好的了解,我想做的范围更广一些,我发现SortedList可能适合: 我想用DateTime键缓存double 具有给定密钥的double的访问性能优于添加密钥和移除密钥的性能 事情是这样的:我必须为给定的密钥范围“失效”缓存(删除密钥)。同样
SortedList
。我想得到给定的高低日期时间间隔的所有键(或者它们的索引范围,应该是一个闭合的整数间隔(错过了什么?)
注:低值和高值实际上不需要在SortedList中
如果有人对如何在没有SortedList的情况下实现这一点有更好的了解,我想做的范围更广一些,我发现SortedList可能适合:
- 我想用DateTime键缓存double
- 具有给定密钥的double的访问性能优于添加密钥和移除密钥的性能
- 事情是这样的:我必须为给定的密钥范围“失效”缓存(删除密钥)。同样,不能保证在缓存中准确找到范围min和max
键运行两次自适应的二进制搜索来解决此问题,以找到绑定键集合中感兴趣范围的索引
由于IList
不提供二进制搜索功能,因此您需要编写自己的程序。幸运的是,还可以选择从中窃取现成的实现
下面是一个用于查找下限的改编版本:
public static int LowerBound<T>(this IList<T> list, T value, IComparer<T> comparer = null)
{
if (list == null)
throw new ArgumentNullException("list");
comparer = comparer ?? Comparer<T>.Default;
int lower = 0, upper = list.Count - 1;
while (lower <= upper)
{
int middle = lower + (upper - lower) / 2;
int comparisonResult = comparer.Compare(value, list[middle]);
// slightly adapted here
if (comparisonResult <= 0)
upper = middle - 1;
else
lower = middle + 1;
}
return lower;
}
到
if(比较结果<0)
现在这样做很简单:
var low = set.Keys.LowerBound(value);
var high = set.Keys.UpperBound(value);
// These extra comparisons are required because the adapted binary search
// does not tell us if it actually found the needle. They could be rolled
// into the methods themselves, but this would require another out parameter.
if (set.Keys[low] != value) ++low;
if (set.Keys[high] != value) --high;
if (low <= high) /* remove keys in the range [low, high] */
var low=set.Keys.LowerBound(值);
var high=设置键上限(值);
//这些额外的比较是必需的,因为自适应的二进制搜索
//没有告诉我们它是否真的找到了针。它们可以滚动
//进入方法本身,但这需要另一个out参数。
如果(设置键[低]!=值)+低;
如果(设置键[高]!=值)--高;
if(low我想知道为什么SortedList
在已经按键排序的情况下不提供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;
}
}
现在,您可以使用extension方法获得高低键之间的键范围:
IEnumerable<DateTime> dateRange = sortedList.GetKeyRangeBetween(low, high).ToList();
请注意,这是从零开始构建的,没有经过真正的测试。因此,您的问题基本上是获取一系列的键?您是否决心使用SortedList
?您是否可以使用SortedSet
和通过DateTime
进行比较的比较器?然后您可以使用GetViewBetween
@Skeet SortedSet>进行二进制比较重新搜索键
,使“下”值在搜索未命中时返回给您。键
支持有效的索引访问,不需要复制来实现,因此一切都应该进行得很顺利。列表中不需要端点。这将不起作用。最坏的情况是列表中的所有键都相等,这将退化为O(n)除非仔细优化二进制搜索以在对数时间内找到范围的端点。SortedList.Keys
是一个IList
,它既不是列表
也不是DateTime[],那么你如何使用<代码> BialSaule< /Cuff>?.G.Pasdou:它可以肯定地工作——当二进制搜索失败时,它已经有效地找到了搜索目标将存在的位置。考虑C++中的,这正是你想要的,是对数。BTW不把它当作抱怨,但我不能相信我。必须在2014年对已排序的内容实施二进制搜索才能获得一个项目what is=Hi@Tim,当您添加for循环以将内容复制到新数组时,它变成了O(n)而不是O(log n)用于二进制搜索。有什么方法可以避免这个副本吗?@Kiran:迟回答总比不回答好。更改了一点扩展名,尝试用反射来解析TKey[]
,并将新数组填充为备用数组。
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;
}
}
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