【问题标题】:Equality for 2 lists of different types2个不同类型列表的平等
【发布时间】:2011-06-03 09:45:28
【问题描述】:

假设我们有 2 个集合,至少 IEnumerable 可以启动 Linq(也假设 .net 4.0):

List<T1> list1;  
List<T2> list2;

我可以定义 T1 和 T2 类型的对象之间的相等性。

  1. 验证 2 个列表是否相等(元素顺序不相关)的最佳方法是什么(即首选 .net 接口和 Linq)。

  2. 如果我知道对象 T1 和 T2 有一个 ID,我该如何优化这个问题

T1 和 T2 的前例:

class Device 
{
    string Id;
    string Name;
}

class DeviceInfo
{ 
    string Identifier;
    string FriendlyName;
    DateTime CreateDate;
}

后期编辑:

解决方案应该涉及我编写的某种相等比较器,并且足够通用。在某些情况下,两个对象具有相同的 Id 但名称不同,那么比较应该会失败。例如:

static bool AreEqual(Device device, DeviceInfo deviceInfo)
{
     return device.Id == deviceInfo.Identifier &&
            device.Name == deviceInfo.FriendlyName;
}

【问题讨论】:

  • IEqualityComparer 应该适用于相同类型的对象。你可以让它接受两个 object 参数并尝试一些强制转换等,但不要:使用正确的工具来完成这项工作。

标签: .net linq interface equality


【解决方案1】:

试试这个来获得两个不同列表之间的区别: 如果他们有任何共同的财产。

 var differentItems = List<Type1>.Select(d => d.Name)
                        .Except(List<Type2>.Select(d => d.Name));

【讨论】:

    【解决方案2】:

    你可以这样做:

    List<Device> devices = ...
    List<DeviceInfo> deviceInfos = ...
    
    var deviceIds = devices.Select(d => d.Id)
                           .OrderBy(id => id);
    
    var deviceInfoIds = deviceInfos.Select(d => d.Identifier)
                                   .OrderBy(id => id);
    
    bool areEqual = deviceIds.SequenceEqual(deviceInfoIds);
    

    如果不可能有重复的 Id,那么 set 语义就会派上用场:

    bool areEqual = !devices.Select(d => d.Id)
                            .Except(deviceInfos.Select(d => d.Identifier))
                            .Any();
    

    如果可能的话,我建议您声明一个IHasId(或类似的)接口并让两种类型都实现它。

    编辑

    作为对您的编辑的回应,您可以编写一个 IEqualityComparer 实现来满足您的需求。它看起来真的很难看;您必须从 each 参数到 DeviceInfo / Device 进行推测性转换,以尝试提取标识符。我不会真的推荐这个;比较完全不同类型的对象对于相等比较器来说是个坏主意。如果您让每种类型都实现一个提供标识符的通用接口,那将会更容易很多

    【讨论】:

    • 您已将问题简化为比较两个字符串列表。如果不是 IEquatable / IEqualityComparer,还有什么?
    【解决方案3】:

    比较两个字符串列表并不是很复杂。两种基于对列表排序的解决方案都具有 N log (N) 复杂性,而不考虑字符串的大小。更好的解决方案是(伪代码),复杂度是N:

    create a dictionary<string, int>
    
    foreach element in list1
    if element is in dict
     dict[element]++;
    else
     dict[element] = 1;
    
    foreach element in list2
    if element is in dict
     dict[element]--;
    else 
     return NOT_EQUAL;
    
    if dict has only 0 values lists are equal
    

    【讨论】:

      【解决方案4】:

      假设 .NET 4.0:

      Foo[] foos = new Foo[];
      Bar[] bars = new Bar[];
      
      var areDifferent = foos.Zip(bars, (foo, bar) => foo.Id == bar.Id).Any(b => !b);
      

      更好的解决方案是检查foosbars 的长度是否相同,并且所有元素都不是null。当然,此示例假定集合已按 Id 排序。

      更新:

      所以,这是所有 LINQy 细节中的“更好的解决方案”:

      var areDifferent = foos.Count() != bars.Count() ||
                         foos.OrderBy(foo => foo.Id)
                         .Zip(
                             bars.OrderBy(bar => bar.Id),
                             (foo, bar) => foo != null && bar != null && foo.Id == bar.Id)
                         .Any(b => !b);
      

      【讨论】:

      • OP 说“元素的顺序不相关”。如果序列相同但排序不同,这将给出假阴性。
      • Zip 可以工作,但我花了几分钟阅读代码。为什么不使用 SequenceEquals?
      • @Bogdan:要使用SequenceEquals,集合需要是相同的类型,或者派生自一个公共基础,或者实现相同的接口。基于Zip 的解决方案允许您将比较函数编写为内联lambda,并且没有这些要求。
      • 好点,从您的代码中看不出应该扩展该条件以包含比较函数。
      猜你喜欢
      • 2013-03-24
      • 2015-04-10
      • 2016-08-14
      • 1970-01-01
      • 2016-01-19
      • 2010-12-16
      • 2010-11-26
      • 1970-01-01
      相关资源
      最近更新 更多