我有一个非高效的方法,我怎样才能提高效率?

时间:2010-11-04 12:35:37

标签: c# performance time-complexity

我有一个简单的方法来比较FileInfo对象数组和文件名列表,以检查已经处理过的文件。然后返回未处理的列表。

此方法的循环迭代大约250,000个FileInfo对象。这需要花费大量的时间来参与竞争。

效率低下显然是对processedFiles集合的Contains方法调用。

首先,我如何检查以确保我对原因的怀疑是肯定的;其次,我如何改进方法以加快处理速度?

public static List<FileInfo> GetUnprocessedFiles(FileInfo[] allFiles, List<string> processedFiles)
{
List<FileInfo> unprocessedFiles = new List<FileInfo>();
foreach (FileInfo fileInfo in allFiles)
{
    if (!processedFiles.Contains(fileInfo.Name))
    {
        unprocessedFiles.Add(fileInfo);
    }
    }
    return unprocessedFiles;
}

6 个答案:

答案 0 :(得分:14)

List<T>的{​​{1}}方法以线性时间运行,因为它可能必须枚举整个列表以证明项目的存在/不存在。我建议您使用Contains或类似代码。 HashSet<T>HashSet<string>方法设计为在Contains时间内运行,即它不应该取决于集合中的项目数。

这个小的改变应该使整个方法在线性时间内运行:

O(1)

如果可能的话,我会建议3项改进:

  1. 对于额外效率,将处理过的文件存储在源的集合中,以便此方法将public static List<FileInfo> GetUnprocessedFiles(FileInfo[] allFiles, List<string> processedFiles) { List<FileInfo> unprocessedFiles = new List<FileInfo>(); HashSet<string> processedFileSet = new HashSet<string>(processedFiles); foreach (FileInfo fileInfo in allFiles) { if (!processedFileSet.Contains(fileInfo.Name)) { unprocessedFiles.Add(fileInfo); } } return unprocessedFiles; } 作为参数。这样,您不必每次都重建该集合。
  2. 尽量不要以这种方式混合和匹配相同实体(ISet<T>string)的不同表示形式。选择一个并继续使用它。
  3. 您可能还想考虑FileInfo方法,而不是自己进行循环。请记住,这会改变集合。
  4. 如果您可以使用LINQ,并且您可以在每次通话时建立一个集合,这是另一种方式:

    HashSet<T>.ExceptWith

答案 1 :(得分:3)

我会尝试将processedFiles列表转换为HashSet。使用列表,每次调用contains时都需要迭代列表。 HashSet是O(1)操作。

答案 2 :(得分:1)

您可以使用字典/ hastable类来显着加快查找过程。甚至将传入的List转换为哈希表一次,然后使用那个将比你正在使用的快得多。

答案 3 :(得分:0)

  1. 按文件名
  2. 对搜索到的数组进行排序
  3. 使用Array.BinarySearch<T>()搜索数组。这应该是大约O(logN)效率。

答案 4 :(得分:0)

使用排序列表检查列表是否包含元素更快

答案 5 :(得分:0)

过分迂腐...

如果您知道两个列表都已排序(FileInfo列表经常预先排序,因此这种方法可能适用于您),那么您可以实现真正的线性性能,而无需哈希集的时间和内存开销。 Hashset构造仍然需要线性时间来构建,因此复杂度更接近于O(n + m);在你的情况下,hashset必须在内部为最多250k字符串分配额外的对象引用,这将花费在GC术语上。

像这种半生不熟的概括可能会有所帮助:

public static IEnumerable<string> GetMismatches(IList<string> fileNames, IList<string> processedFileNames, StringComparer comparer)
    {
        var filesIndex = 0;
        var procFilesIndex = 0;

        while (filesIndex < fileNames.Count)
        {
            if (procFilesIndex >= processedFileNames.Count)
            {
                yield return files[filesIndex++];
            }
            else
            {
                var rc = comparer.Compare(fileNames[filesIndex], processedFileNames[procFilesIndex]);
                if (rc != 0)
                {
                    if (rc < 0)
                    {
                        yield return files[filesIndex++];
                    }
                    else
                    {
                        procFilesIndex++;
                    }
                }
                else
                {
                    filesIndex++;
                    procFilesIndex++;
                }
            }
        }

        yield break;
    }

我非常同意Ani坚持通用或规范类型是非常好的事情。 但是我会给我的-1是未完成的概括,-1是优雅......