列出C#中文件夹内的重复文件:利用LINQ.AsParallel
我已将以下算法编写到C#代码中,以递归方式列出文件夹中的文件。
- 开始迭代目录及其子目录中的文件列表。
- 将文件名称和路径存储在列表中。
- 如果当前文件与列表中的任何其他文件匹配,则在将两个文件标记为重复时。
- 从列表中获取标记为重复的所有文件。
- 按名称分组并返回。
在包含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; } }
- FileTuple类跟踪文件名和包含目录,该标志跟踪重复状态。
- 我已经重写了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#示例如何在你的文件中使用它码。