【问题标题】:How to compare list efficiently?如何有效地比较列表?
【发布时间】:2017-02-23 10:46:27
【问题描述】:

我目前正在asp.net 中开发一个Web 应用程序。在某些 api 调用中,有必要将 ListA 与 ListB 的 ListB 进行比较,以确定 ListA 是否具有 ListB 中任何 List 的相同元素。换句话说:如果 ListA 包含在 ListB 中。

使用 EF-Code-First db 的 Linq 查询这两个集合。 ListB 要么有一个匹配的 List,要么没有,永远不会超过一个。在最坏的情况下,ListB 有数百万个元素,因此比较需要可扩展。

我不是在做嵌套的 foreach 循环,而是在寻找一个纯 linq 查询,它可以让 db 完成工作。 (在我考虑多列索引之前)

为了说明结构:

//In reality Lists are queried of EF 
var ListA = new List<Element>();
var ListB = new List<List<Element>>(); 
List<Element> solution;
bool flag = false;
foreach (List e1 in ListB) {
   foreach(Element e2 in ListA) {
        if (e1.Any(e => e.id == e2.id)) flag = true;
        else {
             flag = false;
             break;
        }
    }
        if(flag) {
           solution = e1;
           break;
        }
}

更新结构

由于它是一个 EF 数据库,我将提供相关的对象结构。我不确定我是否可以发布真实代码,所以这个例子仍然是通用的。

//List B
class Result {
       ...
       public int Id;

       public virtual ICollection<Curve> curves; 

       ...
}

class Curve {
       ...
       public int Id;

       public virtual Result result;
       public int resultId;

       public virtual ICollection<Point> points;
       ...
}
public class Point{
    ...
    public int Id;
    ...
}

控制器(用于 api 调用)想要提供正确的曲线对象。为了识别正确的对象,提供了一个过滤器(ListA)(实际上是一个曲线对象) 现在需要将过滤器 (ListA) 与结果中的曲线列表 (ListB) 进行比较 比较曲线的唯一方法是比较两者都有的点。 (所以事实上比较列表) 曲线大约有 1 - 50 个点。 结果可以有大约 500.000.000 条曲线

这里可以通过 Object-Identity 进行比较,因为所有的 Objects(甚至是过滤器)都会从 db 中重新查询。

我正在寻找一种方法来实现这种机制,而不是如何解决这种情况。 (例如,通过使用多列索引(更改表格))

(用于说明目的):

class controller {
    ...
    public Response serveRequest(Curve filter) {
         foreach(Curve c in db.Result.curves) {
               if(compare(filter.points , c.points)) return c;

         }
    }
}

【问题讨论】:

  • 您的代码无法编译,请仅发布真实代码。 obs:它是var
  • 你需要使用内连接,但不了解结构,很难建议。
  • 这部分可以澄清一下:“最坏情况 ListB 有数百万个元素” - 数百万个元素还是数百万个列表?
  • 这是一个有趣的问题,但由于“百万”这个词,我也会考虑纯 SQL 解决方案或部分 SQL 解决方案。
  • 我认为您应该使用纯 SQL 来解决这个问题,通过适当的索引,应该能够在这么多数据上提供充足的性能。它可以像单个 JOIN 一样简单,结合 COUNT 以确保所有元素都匹配(或任何其他用于集合相等的 SQL 咒语)。

标签: c# entity-framework linq bigdata


【解决方案1】:

使用Except:

    public static bool ContainsAllItems(IList<T> listA, IList<T> listB)
    {
        return !listB.Except(listA).Any();
    }

上面的方法会判断listA是否包含listB的所有元素..并且复杂度比O(n*m)方法快得多。

【讨论】:

  • 如果他指向内存中的相同实例,这将起作用,或者如果他创建了一个IEqualityComparer
【解决方案2】:

试试这个:

bool isIn = ListB.Any(x=>x.Count==ListA.Count && ListA.All(y=>x.Contains(y)));

或者,如果你想要元素

var solution = ListB.FirstOrDefault(x=>x.Count==ListA.Count && ListA.All(y=>x.Contains(y)));

【讨论】:

    【解决方案3】:

    我有东西要给你:

    var db = new MyContext();
    
    var a = db.LoadList(); // or whatever
    var b = new List<IQueryable<Entities>>(db.LoadListOfLists()/*or whatever*/);
    
    b.Any(x => x.Count.Equals(a.Count) & x.All(y => a.Any(z => z.Id == y.Id)));
    

    【讨论】:

      【解决方案4】:

      因为性能是问题,我建议在比较 Ex-之前将您的 listA 转换为查找/字典

      var listALookup = listA.ToLookup(item => item.Id);
      var result = listB.FirstOrDefault(childList => childList.Count == listA.Count && childList.All(childListItem => listALookup.Contains(childListItem.Id)));
      

      Lookup.Contain 是 O(1) 而 List.Contains 是 O(n)

      更好的选择是在 db 级别执行此比较,以减少加载不必要的数据。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-06-12
        • 1970-01-01
        • 2020-04-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多