比较包含大量对象的两个列表(第3部分)“那些对象具有不同的类型”

时间:2011-05-20 22:01:40

标签: c# linq performance

我怎样才能加快这个linq查询?

这需要很长时间,当我在列表中放置大量对象时,会出现内存异常。

List<DirectoryInfo> directoriesThatWillBeCreated = new List<DirectoryInfo>();
// some code to fill the list
// ..
// ..

List<FileInfo> FilesThatWillBeCopied = new List<FileInfo>();
// some code to fill the list
//....

directoriesThatWillBeCreated = (from a in FilesThatWillBeCopied
                                from b in directoriesThatWillBeCreated
                                where a.FullName.Contains(b.FullName)
                                select b).ToList();

我希望我可以做previous solution 之类的事情,但在处理不同类型的对象时我不知道该怎么做。我是否必须创建一个新类,然后将所有FileInfo和DirectoryInfo对象转换为该类,然后执行查询?此外,FileInfo和DirectoryInfo类是密封的,我不能从它们继承,因此我将不得不创建一个新类,这将是无效的。至少这将比该查询更有效,因为该查询需要永远。

3 个答案:

答案 0 :(得分:0)

这很慢,因为代码在目录列表中为每个文件进行线性搜索。试试这个:

var dirlist = FilesThatWillBeCopied
    .Select(f => Directory.GetParent(f.FullName))
    .GroupBy(d => d.FullName)

您可能需要稍微使用语法,但希望您明白这一点。

答案 1 :(得分:0)

您可以做的一件事是将Contains更改为StartsWith。如果匹配失败,StartsWith将更快失败。

directoriesThatWillBeCreated = (from a in FilesThatWillBeCopied
                                from b in directoriesThatWillBeCreated
                                where a.FullName.StartsWith(b.FullName)
                                select b).ToList();

但这不是一个完整的解决方案。如果FilesThatWillBeCopied包含M个项且directoriesThatWillBeCreated包含N个元素,那么您的查询将处理MxN字符串比较。

另一个选项

要尝试的另一个优化,首先遍历directoriesThatWillBeCreated,然后选择与FileInfo中的任何FilesThatWillBeCopied匹配的优化。通过检查是否有任何匹配,您可以在找到匹配项后暂停测试文件。这可以这样做:(警告,记事本代码如下)

directoriesThatWillBeCreated = directoryThatWillBeCreated
    .Select(b => FilesThatWillBeCopied
    .Any(a => a.FullName.StartsWith(b.FullName)));

答案 2 :(得分:0)

我建议使用HashSet<DirectoryInfo>进行比较,但不幸的是,DirectoryInfo没有实现适当的相等比较,因此字符串必须这样做。 (另一个选择是实现自己的IComparer<DirectoryInfo>。)此外,您应该在名称上使用StringComparer.InvariantCultureIgnoreCase,除非您确定两个集合具有相同的大小写。

var dirs = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
// fill dirs

var files = new List<FileInfo>();
// fill files

var result = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);

foreach (var file in files)
{
    var dir = file.Directory;
    while (dir != null && !result.Contains(dir.FullName))
    {
        if (dirs.Contains(dir.FullName))
            result.Add(dir.FullName);
        dir = dir.Parent;
    }
}

这个解决方案根本不使用LINQ,但是当你追求性能并且最直接的LINQ解决方案太慢时,通常会出现这种情况。