我正在研究一个项目(在.NET 3.5中),该项目读入2个文件,然后比较它们并找到丢失的对象。
根据这些数据,我需要进一步解析它并找到对象位置。我会尝试进一步解释:
我有2个名单: 1列表是服务器上所有文件的一个很长的列表,以及它们在服务器或其他服务器上的物理地址,这个文件长度超过10亿行并且不断增长(我知道这个文件更为荒谬)。目前文件大小约为160MB。 另一个列表是一个报告列表,显示服务器上缺少的文件。与列表1相比,此列表微不足道,通常小于1MB。
我必须将列表2与列表1相交,并确定丢失的对象所在的位置。列表中的项目看起来像这样(不幸的是它是空格分隔的而不是CSV文档): filename.extension rev rev#source server:harddriveLocation \ | filenameOnServer.extension origin
使用流,我将两个文件读入单独的字符串列表。然后我拿一个正则表达式并将列表2中的项解析成包含filename.extension,rev和rev#的第三个列表。所有这一切都非常有效,它的表现正在扼杀我。
我希望有一种更有效的方式来做我正在做的事情。
Orange
这样可行,但由于我的遗失物品清单中目前缺少1300件物品,因此平均需要8到12分钟才能完成。花费时间最长的部分是
foreach (String item in slMissingObjectReport)
{
if (item.Contains(".ext1") || item.Contains(".ext2") || item.Contains(".ext3"))
{
if (!item.Contains("|"))
{
slMissingObjects.Add(item + "," + slMissingObjectReport[i + 1] + "," + slMissingObjectReport[i + 2]); //object, rev, version
}
}
i++;
}
int j = 1; //debug only
foreach (String item in slMissingObjects)
{
IEnumerable<String> found = Enumerable.Empty<String>();
Stopwatch matchTime = new Stopwatch(); //used for debugging
matchTime.Start(); //start the stop watch
foreach (String items in slAllObjects.Where(s => s.Contains(item.Remove(item.IndexOf(',')))))
{
slFoundInAllObjects.Add(item);
}
matchTime.Stop();
tsStatus.Text = "Missing Object Count: " + slMissingObjects.Count + " | " + "All Objects count: " + slAllObjects.Count + " | Time elapsed: " + (taskTime.ElapsedMilliseconds) * 0.001 + "s | Items left: " + (slMissingObjects.Count - j).ToString();
j++;
}
taskTime.Stop();
lstStatus.Items.Add(("Time to complete all tasks: " + (taskTime.ElapsedMilliseconds) * 0.001) + "s");
我只需要一个指向正确方向的点,也许还有一个关于如何改进我正在研究的代码的方法。 LINQ似乎并不是杀手锏,它将它添加到一个似乎会破坏性能的列表中。
答案 0 :(得分:4)
Hashsets专为此类任务而设计,您可以在其中使用唯一值进行比较。
列表,不是。它们只是任意的集合。
我的第一个停靠点是使用HashSet&lt;&gt;以及随之而来的各种交叉方法。
答案 1 :(得分:2)
您可以使用AddRange
代替Add
进行改进。 AddRange
将允许内部列表预先分配添加所需的内存,而不是在foreach
循环的整个过程中多次。
IEnumerable<string> items = slAllObjects.Where(s => s.Contains(item.Remove(item.IndexOf(','));
slFoundInAllObjects.AddRange(items);
其次,您应该避免item.Remove(item.IndexOf(',')
lambda中的Where
,因为这会导致它对列表中的每个项目执行一次。这个值是静态的,你可以提前做一次。
var itemWithoutComma = item.Remove(item.IndexOf(','));
IEnumerable<string> items = slAllObjects.Where(s => s.Contains(itemWithoutComma));
slFoundInAllObjects.AddRange(items);
答案 2 :(得分:1)
似乎已经指出了一些瓶颈。
如果我理解你是:
所以你有点订单:O(K + m * n * n)
。
瓶颈发生在第2步和第3步(代码中的内部循环)。
解决方案:
如果您使用哈希集,则此解决方案应将O(n^2) * O(m)
减少为O(n) * O(k)
,如果您对列表进行排序,则应将O(n) * log(m)
减少。
答案 3 :(得分:0)
首先停止,不要使用List。使用HashSets可以更快地插入和比较。
接下来,确定列表是否处于预先排序的顺序,如果是,则可以同时快速读取这两个文件,并且只进行一次通过,而不必将它们保存在内存中一点都不。
如果所有其他方法都失败了,请考虑使用LINQ的Intersects方法,该方法可能比你的本土版本更好。
答案 4 :(得分:0)
除了已经提出的建议外,我还会考虑使用树木。如果我理解正确,文件名中有某种层次结构(即:服务器,文件路径,文件名等),对吗?通过使用树,您可以在每个步骤中减少很多搜索空间。
此外,如果您在每个节点中使用Dictionary<String, Node>
,则可以减少搜索时间,考虑到常量的层次结构级别,搜索时间将变为O(1)
。
此外,如果您决定使用数组或数组列表,请避免使用foreach
并使用for
,因为它应该更快(不使用迭代器,因此,对于数组列表至少应该更快)
如果有什么不清楚,请告诉我。