首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >LINQ到SortedList

LINQ到SortedList
EN

Stack Overflow用户
提问于 2010-05-24 22:36:17
回答 5查看 4.2K关注 0票数 8

我是一个完全的LINQ新手,所以我不知道我的LINQ对于我需要做的事情是不是不正确,或者是我对性能的期望太高了。

我得到了一个对象的SortedList,键值是int;SortedList,而不是SortedDictionary,因为我将用预先排序的数据填充这个集合。我的任务是找到确切的键,或者,如果没有确切的键,找到具有下一个较高值的键。如果搜索对列表来说太高(例如,最高键是100,但搜索105),则返回null。

代码语言:javascript
复制
// The structure of this class is unimportant.  Just using
// it as an illustration.
public class CX
{
    public int KEY;
    public DateTime DT;
}

static CX getItem(int i, SortedList<int, CX> list)
{
    var items =
    (from kv in list
     where kv.Key >= i
     select kv.Key);

    if (items.Any())
    {
        return list[items.Min()];
    }

    return null;
}

给定一个包含50,000条记录的列表,调用getItem 500次大约需要1.5秒。调用它50,000次需要超过2分钟。这个性能看起来很差。我的LINQ是不是坏了?我是不是期望太高了?我应该使用我自己的二进制搜索函数吗?

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2010-05-25 03:27:40

自己编写二进制搜索可能会很困难。

幸运的是,微软已经编写了一个相当健壮的:Array.BinarySearch<T>This is, in fact, the method that SortedList.IndexOfKey uses internally。唯一的问题是,它需要一个T[]参数,而不是任何IList<T> (如SortedList<TKey, TValue>.Keys)。

你知道吗?有一个很棒的工具叫做Reflector,它可以让你查看.NET源代码……

看看:IList<T>上的一个通用BinarySearch扩展方法,直接取自微软Array.BinarySearch<T>实现的反射代码。

代码语言:javascript
复制
public static int BinarySearch<T>(this IList<T> list, int index, int length, T value, IComparer<T> comparer) {
    if (list == null)
        throw new ArgumentNullException("list");
    else if (index < 0 || length < 0)
        throw new ArgumentOutOfRangeException((index < 0) ? "index" : "length");
    else if (list.Count - index < length)
        throw new ArgumentException();

    int lower = index;
    int upper = (index + length) - 1;

    while (lower <= upper) {
        int adjustedIndex = lower + ((upper - lower) >> 1);
        int comparison = comparer.Compare(list[adjustedIndex], value);
        if (comparison == 0)
            return adjustedIndex;
        else if (comparison < 0)
            lower = adjustedIndex + 1;
        else
            upper = adjustedIndex - 1;
    }

    return ~lower;
}

public static int BinarySearch<T>(this IList<T> list, T value, IComparer<T> comparer) {
    return list.BinarySearch(0, list.Count, value, comparer);
}

public static int BinarySearch<T>(this IList<T> list, T value) where T : IComparable<T> {
    return list.BinarySearch(value, Comparer<T>.Default);
}

这将允许您调用list.Keys.BinarySearch,并在未找到所需键的情况下获得所需索引的负数位补码(以下内容基本上直接取自tzaman的答案):

代码语言:javascript
复制
int index = list.Keys.BinarySearch(i);
if (index < 0)
    index = ~index;
var item = index < list.Count ? list[list.Keys[index]] : null;
return item;
票数 5
EN

Stack Overflow用户

发布于 2010-05-24 22:41:50

首先,查询被评估了两次(一次针对Any,一次针对Min)。其次,Min要求遍历整个列表,即使它是排序的,这意味着第一项将是最小项。您应该能够更改此设置:

代码语言:javascript
复制
if (items.Any())
{
    return list[items.Min()];
}

要这样做:

代码语言:javascript
复制
var default = 
    (from kv in list
     where kv.Key >= i
     select (int?)kv.Key).FirstOrDefault();

if(default != null) return list[default.Value];

return null;

更新

因为您选择的是值类型,所以FirstOrDefault不会返回可以为空的对象。我更改了您的查询,改为将所选值转换为int?,从而允许检查null的结果值。我主张这样做而不是使用ContainsKey,因为如果您的列表包含0的值,那么它将返回true。例如,假设您有以下值

0 2 4 6 8

如果您要传入任何小于或等于8的值,那么您将获得正确的值。然而,如果你传入9,你会得到0 (default(int)),它在列表中,但不是一个有效的结果。

票数 7
EN

Stack Overflow用户

发布于 2010-05-24 22:39:23

SortedList上使用LINQ不会给你带来排序的好处。

为了获得最佳性能,您应该编写自己的二进制搜索。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/2897749

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档