我正在尝试从网站上删除不需要的图片。产品图像文件夹包含超过200000个图像。我有一个在列表中处于非活动状态的产品代码列表。我有另一个列表中的文件名列表。
List<string> lFileList = files.ToList();
List<string> lNotinfiles = new List<string>();
foreach (var s in lFileList)
{
var s2 = (from s3 in lProductsList
where s.Contains(s3.cProductCode)
select s3.cProductCode).FirstOrDefault();
if (s2 == null)
{
lNotinfiles.Add(s);
}
}
此处lProductsList
是包含未使用的ProductCodes
的列表。
图像列表包含同一产品的多个图像,但图像名称包含产品代码(主要是它开头,可能有_1,_2.jpg将在那里。
以上代码可以正常工作,但单个文件夹获取Not in列表需要5分钟以上。我确实尝试了以下但是花了超过15分钟。
var s2 = (from s3 in lProductsList
where s.IndexOf(s3.cProductCode) >= 0
select s3.cProductCode).FirstOrDefault();
我试图将所有循环一起删除也无效。
什么应该是更快实现这一目标的最佳方法。
答案 0 :(得分:2)
我建议:使用HashSet
,等待ToList
,也许GroupBy
。
HashSet
+使用ToList
目前你的代码的时间复杂度为o(n)2 - 你迭代外部列表,每个项目迭代内部列表的所有项目。
将lProductsList
的类型从列表更改为包含HashSet<string>
的代码。查找HashSet
中的项目是o(1)(列表是o(n))。然后当你迭代lFileList
的每一次以查找它们是否在lProductsList
时,它将是o(n)的时间复杂度而不是o(n)2。
此代码将显示使用2个列表或使用列表与HashSet之间的时差:
var items = (new[] { "1", "2", "3","4","5","6","7","8","9","10" }).SelectMany(x => Enumerable.Repeat(x, 10000)).ToList();
var itemsToFilterOut = new List<string> { "1", "2", "3" };
var efficientItemsToFilterOut = new HashSet<string>(itemsToFilterOut);
var watch = System.Diagnostics.Stopwatch.StartNew();
var unwantedItems = items.Where(item => itemsToFilterOut.Contains(item)).ToList();
watch.Stop();
Console.WriteLine(watch.TotalMilliseconds);
watch = Stopwatch.StartNew();
var efficientUnwantedItems = items.Where(item => efficientItemsToFilterOut.Contains(item)).ToList();
watch.Stop();
Console.WriteLine(watch.TotalMilliseconds);
至于将其放在代码的上下文中:
var notInUseItems = new HashSet(from item in lProductsList
select item.cProductCode);
//Notice that here I am not using the materialized `lFileList`
lNotinfiles = files.Where(item => !notInUseItems.Contains(item));
的GroupBy
此外 - 您说该列表包含映射到同一个键的多个项目。在过滤之前使用GroupBy
。检查此添加的性能:
watch = Stopwatch.StartNew();
var moreEfficientUnwantedItems = items.GroupBy(item => item)
.Where(group => efficientItemsToFilterOut.Contains(group.Key))
.Select(group => group.Key);
watch.Stop();
Console.WriteLine(watch.TotalMilliseconds);
检查您的数据,以分析重复数量的重要程度,并在需要时使用GroupBy
答案 1 :(得分:1)
两个建议:
.ToList()
,即不要等到检索所有文件NotInFiles
组织为HashSet<String>
以获得更好的合并O(1)
而不是O(N)
。这样的事情:
//TODO: you have to implement this
prtivate static String ExtractProductCode(string fileName) {
int p = fileName.IndexOf('_');
if (p >= 0)
return fileName.SubString(0, p);
else
return fileName;
}
...
HashSet<String> NotInFiles = new HashSet<String>(
lNotinfiles,
StringComparer.OrdinalIgnoreCase); // file names are case insensitive
...
var files = Directory
.EnumerateFiles(@"C:\MyPictures", "*.jpeg", SearchOption.AllDirectories)
.Where(path => Path.GetFileNameWithoutExtension(path))
.Select(path => ExtractProductCode(path))
.Where(code => !NotInFiles.Contains(code))
.ToList(); // if you want List materialization
答案 2 :(得分:0)
您正在将您的(我假设)数组转换为List,然后执行foreach 直接在数组上使用for应该会使它至少快一点。
List<string> lNotinfiles = new List<string>();
for(int i = 0; i < files.Count(); i++)
foreach (var s in files)
{
var s2 = (from s3 in lProductsList where s.Contains(s3.cProductCode) select s3.cProductCode).FirstOrDefault();
if (s2 == null)
{
lNotinfiles.Add(s);
}
}