1

我很难通过以下示例切换到 LINQ:

int[] inputs = { 5, 3, 5, 66, 4, 5 };
int[] indexes; // I want to have the indexes of '5's in 
               // the inputs array, which is { 0, 2, 5 }

// My way (traditional for loop)

List<int> indexesList = new List<int>();
for (int i = 0; i < inputs.Length; i++)
    if (inputs[i] == 5)
        indexesList.Add(i);

indexes = indexesList.ToArray();

// LINQ way

var indexes = inputs.Select((s, i) => new { i, s })
                    .Where(t => t.s == 5)
                    .Select(t => t.i).ToArray();

问题 1.效率(速度、内存使用)方面,如果我将代码转换为 LINQ,我会有什么优势吗?

问题 2.如果属实,是否有更优雅的方式来使用 LINQ?

PS:注意,这个方法在我的实际项目中调用非常频繁。因此,在速度或内存使用方面稍有改进将对整个过程有很大帮​​助。

4

4 回答 4

2

如果您使用 LINQ 代码,您肯定不会获得性能优势。只需阅读它就可以看出这一点:为了将每个值与其索引相关联,创建了一个新对象:

(s, i) => new { i, s }

这些匿名对象只是将索引与价值粘合在一起的容器。因此,与直接保持计数器相比,所有相关的内存管理都是纯粹的开销。

有人可能会说 LINQ 提供了可读性优势,因为大多数时候它强调的是意图而不是机制,但在这种特殊情况下,我认为它并不比平淡无奇的解决方案更好。

于 2012-11-26T19:56:18.427 回答
1

如果您想以 LINQ 方式执行此操作,我建议您实现自己的扩展方法来返回与谓词匹配的元素的索引。

public static IEnumerable<int> IndexesWhere<TSource>(
    this IEnumerable<TSource> source, 
    Func<TSource, bool> predicate)
{
    int i = 0;

    foreach (TSource element in source)
    {
        if (predicate(element))
            yield return i;

        i++;
    }
}

然后你可以这样称呼它:

var indexes = inputs.IndexesWhere(s => s == 5);

使用这种方法的优点是:

  1. 您避免创建匿名类型的实例(与Select((s, i) => new { i, s })方法不同)。
  2. 该序列可以按需枚举,这意味着如果您只想执行类似indexes.Take(2).
于 2012-11-26T19:59:04.443 回答
1

这是一种使用 LINQ 并避免创建匿名对象的方法。

var indexes = Enumerable.Range(0, inputs.Length)
                        .Where(x => inputs[x] == 5)
                        .ToArray();

它的性能可能类似于使用 for 循环,但您必须对其进行测试才能确定。

于 2012-11-26T20:15:00.160 回答
1

我会说那里的 linq 方式实际上效率较低,因为您首先将结构转换为元组集合,然后选择索引。

尽管它可能看起来更优雅,但我也想说理解那里到底发生了什么有点困难。

我个人只是推荐迭代。


如果您真的需要提高效率,如果可行的话,我建议您对输入进行排序。第一次对它们进行排序时,需要一些时间,但如果您多次搜索同一个列表,它会收回成本。

于 2012-11-26T19:57:09.767 回答