【问题标题】:Compare two lists that contain a lot of objects (3th part) "those objects have different type"比较两个包含大量对象的列表(第 3 部分)“这些对象具有不同的类型”
【发布时间】:2011-05-24 00:24:40
【问题描述】:

我怎样才能加快这个 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 类是密封的,我不能从它们继承,因此我必须创建一个新类,这样效率不高。至少这将比该查询更有效,因为该查询需要永远。

【问题讨论】:

  • 你试过b.FullName.Contains(a.FullName)之类的吗?

标签: c# linq performance


【解决方案1】:

您可以做的一件事是将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,然后选择与FilesThatWillBeCopied 中的任何FileInfo 匹配的那些。通过检查是否有任何匹配项,一旦找到匹配项,您就可以中断对文件的测试。可以这样做:(警告,记事本代码如下)

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

【讨论】:

    【解决方案2】:

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

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

    您可能需要稍微了解一下语法,但希望您能明白这一点。

    【讨论】:

      【解决方案3】:

      我建议使用HashSet&lt;DirectoryInfo&gt; 进行比较,但不幸的是,DirectoryInfo 没有实现适当的相等比较,因此必须使用字符串。 (另一种选择是实现您自己的IComparer&lt;DirectoryInfo&gt;。)此外,您应该在名称上使用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 解决方案太慢时,通常会出现这种情况。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多