C# 在SortedList中获取两个键之间的所有键的最快方法是什么?

C# 在SortedList中获取两个键之间的所有键的最快方法是什么?,c#,.net,C#,.net,给定一个填充的SortedList。我想得到给定的高低日期时间间隔的所有键(或者它们的索引范围,应该是一个闭合的整数间隔(错过了什么?) 注:低值和高值实际上不需要在SortedList中 如果有人对如何在没有SortedList的情况下实现这一点有更好的了解,我想做的范围更广一些,我发现SortedList可能适合: 我想用DateTime键缓存double 具有给定密钥的double的访问性能优于添加密钥和移除密钥的性能 事情是这样的:我必须为给定的密钥范围“失效”缓存(删除密钥)。同样

给定一个填充的
SortedList
。我想得到给定的高低日期时间间隔的所有键(或者它们的索引范围,应该是一个闭合的整数间隔(错过了什么?)

注:低值和高值实际上不需要在SortedList中


如果有人对如何在没有SortedList的情况下实现这一点有更好的了解,我想做的范围更广一些,我发现SortedList可能适合:

  • 我想用DateTime键缓存double
  • 具有给定密钥的double的访问性能优于添加密钥和移除密钥的性能
  • 事情是这样的:我必须为给定的密钥范围“失效”缓存(删除密钥)。同样,不能保证在缓存中准确找到范围min和max

在对列表进行排序后,您可以使用它来定位间隔的端点。最坏情况下的性能为O(log n)。

您可以通过对
键运行两次自适应的二进制搜索来解决此问题,以找到绑定
集合中感兴趣范围的索引

由于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