列出C#中文件夹内的重复文件:利用LINQ.AsParallel

我已将以下算法编写到C#代码中,以递归方式列出文件夹中的文件。

  1. 开始迭代目录及其子目录中的文件列表。
  2. 将文件名称和路径存储在列表中。
  3. 如果当前文件与列表中的任何其他文件匹配,则在将两个文件标记为重复时。
  4. 从列表中获取标记为重复的所有文件。
  5. 按名称分组并返回。

在包含50,000个文件和12,000个子目录的文件夹上实现速度非常慢。 由于磁盘读取操作基本上是耗时的任务。 即使LINQ.Parallel()也没有多大帮助。

Implmentation:

class FileTuple { public string FileName { set; get; } public string ContainingFolder { set; get; } public bool HasDuplicate { set; get; } public override bool Equals(object obj) { if (this.FileName == (obj as FileTuple).FileName) return true; return false; } } 
  1. FileTuple类跟踪文件名和包含目录,该标志跟踪重复状态。
  2. 我已经重写了equals方法,只比较fileTuples集合中的文件名。

以下方法查找重复文件并作为列表返回。

  private List FindDuplicates() { List fileTuples = new List(); //Read all files from the given path List enumeratedFiles = Directory.EnumerateFiles(txtFolderPath.Text, "*.*", SearchOption.AllDirectories).Where(str => str.Contains(".exe") || str.Contains(".zip")).AsParallel().ToList(); foreach (string filePath in enumeratedFiles) { var name = Path.GetFileName(filePath); var folder = Path.GetDirectoryName(filePath); var currentFile = new FileTuple { FileName = name, ContainingFolder = folder, HasDuplicate = false, }; int foundIndex = fileTuples.IndexOf(currentFile); //mark both files as duplicate, if found in list //assuming only two duplicate file if (foundIndex != -1) { currentFile.HasDuplicate = true; fileTuples[foundIndex].HasDuplicate = true; } //keep of track of the file navigated fileTuples.Add(currentFile); } List duplicateFiles = fileTuples.Where(fileTuple => fileTuple.HasDuplicate).Select(fileTuple => fileTuple).OrderBy(fileTuple => fileTuple.FileName).AsParallel().ToList(); return duplicateFiles; } 

你能否提出一种提高性能的方法。

谢谢您的帮助。

你能否提出一种提高性能的方法。

一个明显的改进是使用Dictionary以及List 。 这样,每次检查都不会有O(N) IndexOf操作。 请注意,您还需要覆盖GetHashCode() – 您应该已经有关于此的警告。

我怀疑它会产生很大的不同 – 我希望这主要是IO限制。

此外,我怀疑最终的过滤和排序将成为一个重要的瓶颈,因此在最后一步中使用AsParallel不太可能。 当然,你应该测量所有这些。

最后,整个方法可以变得相当简单,甚至不需要HasDuplicate标志或任何覆盖Equals / GetHashCode

 private List FindDuplicates() { return Directory.EnumerateFiles(txtFolderPath.Text, "*.*", SearchOption.AllDirectories) .Where(str => str.Contains(".exe") || str.Contains(".zip") .Select(str => new FileTuple { FileName = Path.GetFileName(str), ContainingFolder = Path.GetDirectoryName(str)) }) .GroupBy(tuple => tuple.FileName) .Where(g => g.Count() > 1) // Only keep duplicates .OrderBy(g => g.Key) // Order by filename .SelectMany(g => g) // Flatten groups .ToList(); } 

如果性能至关重要,我可以建议使用来自http://www.voidtools.com/download.php的第三方库,尝试下载此工具并运行一些查询,它将快速点亮,它通过构建索引来工作第一次运行时整个文件系统上的文件和目录,索引在一分钟内构建得非常快,并且在内存和磁盘上都需要一些时间,但在那之后查询会很快,你可以查看他们的C#示例如何在你的文件中使用它码。