我正在开发一个能够找到文件夹之间文件差异的程序。我已经创建了一个遍历给定文件夹的文件夹结构的方法,并为每个子文件夹构建一个树。每个节点都包含一个文件列表,即该文件夹中的文件。每个节点都有一定数量的子节点,对应于该文件夹中的文件夹。
现在的问题是找到一棵树中存在的文件,而不是另一棵树中存在的文件。我有一个方法:“私有列表Diff(节点索引1,节点索引2)”,它应该这样做。但问题是我比较树木的方式。比较两棵树需要花费大量时间 - 当每个输入节点包含大约70,000个文件时,Diff方法大约需要3-5分钟才能完成。
我目前正在这样做:
private List<MyFile> Diff(Node index1, Node index2)
{
List<MyFile> DifferentFiles = new List<MyFile>();
List<MyFile> Index1Files = FindFiles(index1);
List<MyFile> Index2Files = FindFiles(index2);
List<MyFile> JoinedList = new List<MyFile>();
JoinedList.AddRange(Index1Files);
JoinedList.AddRange(Index2Files);
List<MyFile> JoinedListCopy = new List<MyFile>();
JoinedListCopy.AddRange(JoinedList);
List<string> ChecksumList = new List<string>();
foreach (MyFile m in JoinedList)
{
if (ChecksumList.Contains(m.Checksum))
{
JoinedListCopy.RemoveAll(x => x.Checksum == m.Checksum);
}
else
{
ChecksumList.Add(m.Checksum);
}
}
return JoinedListCopy;
}
Node类看起来像这样:
class Node
{
private string _Dir;
private Node _Parent;
private List<Node> _Children;
private List<MyFile> _Files;
}
答案 0 :(得分:4)
不是通过List
结构进行大量搜索(这很慢),您可以将所有校验和放入HashSet
,这可以更有效地 搜索。
private List<MyFile> Diff(Node index1, Node index2)
{
var Index1Files = FindFiles(index1);
var Index2Files = FindFiles(index2);
//this is all of the files in both
var intersection = new HashSet<string>(Index1Files.Select(file => file.Checksum)
.Intersect(Index2Files.Select(file => file.Checksum)));
return Index1Files.Concat(Index2Files)
.Where(file => !intersection.Contains(file.Checksum))
.ToList();
}
答案 1 :(得分:1)
怎么样:
public static IEnumerable<MyFile> FindUniqueFiles(IEnumerable<MyFile> index1, IEnumerable<MyFile> index2)
{
HashSet<string> hash = new HashSet<string>();
foreach (var file in index1.Concat(index2))
{
if (!hash.Add(file.Checksum))
{
hash.Remove(file.Checksum);
}
}
return index1.Concat(index2).Where(file => hash.Contains(file.Checksum));
}
这将假设一棵树不包含副本。 Servy的答案适用于所有情况。
答案 2 :(得分:0)
您是否为树中的每个元素保留了整个FileSystemObject?如果是这样,我认为你的内存开销将是巨大的。为什么不使用文件名或校验和并将其放入列表中,然后对其进行比较?
答案 3 :(得分:0)
我可以看到这不仅仅是一个“不同”的函数,你真正需要的是所有只在JoinedListCopy集合中存在一次的实例,而不仅仅是JoinedListCopy集合中所有不同实例的列表。 / p>
Servy有一个非常好的答案,我建议采用不同的方法,利用linq的一些更有趣的功能,或者至少我发现它们很有趣。
var diff_Files = (from a in Index1Files
join b in Index2Files
on a.CheckSum equals b.CheckSum
where !(Index2Files.Contains(a) || Index1Files.Contains(b))).ToList()
另一种构造“where”的方法,可能效果更好,文件实例可能实际上并不相同,只要涉及代码相等...
where !(Index2Files.Any(c=>c.Checksum == a.Checksum) || Index1Files.Any(c=>c.Checksum == b.Checksum))
查看单个校验和,而不是整个文件对象实例。
基本策略基本上就是你正在做的事情,只是效率更高:加入集合并相互过滤它们,以确保你只获得独特的条目。
另一种方法是使用linq中的计数功能
var diff_Files = JoinedListCopy.Where(a=> JoinedListCopy.Count(b=>b.CheckSum == a.CheckSum) == 1).ToList();
嵌套的linq并不总是世界上最有效的东西,但它应该运行得相当好,让所有实例只出现一次。我最喜欢这种方法,最简单的方法是把事情弄得一团糟,但我先使用的连接可能更有效率。