我有一个简单的方法来比较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;
}
答案 0 :(得分:14)
List<T>
的{{1}}方法以线性时间运行,因为它可能必须枚举整个列表以证明项目的存在/不存在。我建议您使用Contains
或类似代码。 HashSet<T>
的HashSet<string>
方法设计为在Contains
时间内运行,即它不应该取决于集合中的项目数。
这个小的改变应该使整个方法在线性时间内运行:
O(1)
如果可能的话,我会建议3项改进:
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;
}
作为参数。这样,您不必每次都重建该集合。ISet<T>
和string
)的不同表示形式。选择一个并继续使用它。FileInfo
方法,而不是自己进行循环。请记住,这会改变集合。如果您可以使用LINQ,并且您可以在每次通话时建立一个集合,这是另一种方式:
HashSet<T>.ExceptWith
答案 1 :(得分:3)
我会尝试将processedFiles列表转换为HashSet。使用列表,每次调用contains时都需要迭代列表。 HashSet是O(1)操作。
答案 2 :(得分:1)
您可以使用字典/ hastable类来显着加快查找过程。甚至将传入的List转换为哈希表一次,然后使用那个将比你正在使用的快得多。
答案 3 :(得分:0)
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是优雅......