【问题标题】:linq Except and custom IEqualityComparerlinq 除外和自定义 IEqualityComparer
【发布时间】:2010-03-25 03:59:59
【问题描述】:

我正在尝试在两个字符串列表上实现自定义比较器,并使用 .Except() linq 方法来获取不是列表之一的那些。我做一个自定义比较器的原因是因为我需要做一个“模糊”比较,即一个列表上的一个字符串可以嵌入另一个列表上的一个字符串中。

我做了以下比较器

public class ItemFuzzyMatchComparer : IEqualityComparer<string>
{
    bool IEqualityComparer<string>.Equals(string x, string y)
    {
        return (x.Contains(y) || y.Contains(x));
    }

    int IEqualityComparer<string>.GetHashCode(string obj)
    {
        if (Object.ReferenceEquals(obj, null))
            return 0;
        return obj.GetHashCode();
    }
}

当我调试时,唯一命中的断点是在 GetHashCode() 方法中。 Equals() 永远不会被触及。有什么想法吗?

【问题讨论】:

  • 对我来说这是一个很好的练习。在我的情况下,我逃脱了public int GetHashCode(string obj) {return obj.ToLower().GetHashCode();} 你的问题很老,但我在 4 年后遇到了同样的问题。

标签: c# iequalitycomparer


【解决方案1】:

如果返回的所有哈希码都不同,则永远不需要比较是否相等。

基本上问题在于您的哈希和相等概念非常不同。我不完全确定你会如何纠正这个问题,但在你这样做之前它肯定不会起作用。

您需要确保如果Equals(a, b) 返回真,那么GetHashCode(a) == GetHashCode(b)。 (反过来不一定是真的 - 哈希冲突是可以接受的,但显然你希望它们尽可能少。)

【讨论】:

  • 我开始认为这是尝试对一组预定义对象(即字符串)应用自定义比较的情况。如果我必须收集自定义对象,那么我可能会让它工作。我想我必须想出比这更好的方法。 :( 我将把这个问题搁置一天,看看是否有其他人有建议。
  • 我最终分两步完成。我使用包含生成了一个部分匹配的项目列表,然后转身并使用该子集对第一个列表进行了除此之外的操作。感谢您的帮助。
【解决方案2】:

正如 Jon 所指出的,您需要确保两个字符串的哈希码相等(根据您的比较规则)。不幸的是,这非常困难。

为了演示这个问题,Equals(str, "") 对所有字符串 str 返回 true,这实质上意味着所有字符串都等于一个空字符串,因此,所有字符串必须具有与空字符串相同的哈希码.因此,正确实现IEqualityComparer 的唯一方法是始终返回相同的哈希码:

public class ItemFuzzyMatchComparer : IEqualityComparer<string>  { 
  bool IEqualityComparer<string>.Equals(string x, string y)  { 
    return (x.Contains(y) || y.Contains(x)); 
  }  
  int IEqualityComparer<string>.GetHashCode(string obj)  { 
    if (Object.ReferenceEquals(obj, null)) return 0; 
    return 1; 
  } 
}

然后您可以使用Except 方法,它会正常运行。唯一的问题是您(可能)会得到一个非常低效的实现,所以如果您需要更好的性能,您可能必须实现自己的Except。但是,我不确定 LINQ 实现的效率有多低,我不确定是否真的有可能为您的比较规则提供任何有效的实现。

【讨论】:

    【解决方案3】:

    也许这个问题可以在没有 IEqualityComparer 接口实现的情况下解决。 Jon 和 Thomas 对实现该接口有很好的看法,而平等似乎并不能定义您的问题。根据您的描述,我认为您可以在比较期间不使用除扩展名来执行此操作。相反,首先获取匹配项,然后执行例外。看看这是否适合你:

     List<String> listOne = new List<string>(){"hard", "fun", "code", "rocks"};
     List<String> listTwo = new List<string>(){"fund", "ode", "ard"};
    
     var fuzzyMatchList = from str in listOne
                          from sr2 in listTwo
                          where str.Contains(sr2) || sr2.Contains(str)
                          select str;
     var exceptList = listOne.Except(fuzzyMatchList);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多