3

我有两种方法可以获取一个字符串并删除任何“无效”字符(哈希集中包含的字符)。一种方法使用 Linq.Where,另一种方法使用带字符数组的循环。

Linq 方法花费的时间(208756.9 滴答声)几乎是循环(108688.2 滴答刻)的两倍

林克:

    string Linq(string field)
    {
        var c = field.Where(p => !hashChar.Contains(p));
        return new string(c.ToArray());
    }

环形:

    string CharArray(string field)
    {
        char[] c = new char[field.Length];
        int count = 0;

        for (int i = 0; i < field.Length; i++)
            if (!hashChar.Contains(field[i]))
            {
                c[count] = field[i];
                count++;
            }

        if (count == 0)
            return field;

        char[] f = new char[count];

        Buffer.BlockCopy(c, 0, f, 0, count * sizeof(char));

        return new string(f);
    }

我的期望是 LINQ 会击败或者至少可以与循环方法相媲美。循环方法甚至没有优化。我一定在这里遗漏了一些东西。

Linq.Where 如何在幕后工作,为什么它会输给我的方法?

4

1 回答 1

5

如果Mono中的源代码ToArray有任何迹象,那么您的实现会获胜,因为它执行的分配更少(向下滚动到第 2874 行以查看该方法)。

与 LINQ 的许多方法一样,该ToArray方法包含用于集合和其他可枚举的单独代码路径:

TSource[] array;
var collection = source as ICollection<TSource>;
if (collection != null) {
    ...
    return array;
}

在您的情况下,不采用此分支,因此代码继续执行此循环:

int pos = 0;
array = EmptyOf<TSource>.Instance;
foreach (var element in source) {
    if (pos == array.Length) {
        if (pos == 0)
            array = new TSource [4];
        else
            // If the number of returned character is significant,
            // this method will be called multiple times
            Array.Resize (ref array, pos * 2);
    }
    array[pos++] = element;
}

if (pos != array.Length)
    Array.Resize (ref array, pos);

return array;

如您所见,LINQ 的版本可能会多次分配和重新分配数组。另一方面,您的实现只进行两次分配 - 最前面的一个是最大大小,最后一个是复制数据的地方。这就是您的代码更快的原因。

于 2013-08-15T18:33:58.047 回答