19

我正在尝试在两个字符串列表上实现自定义比较器,并使用 .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() 永远不会被触及。有任何想法吗?

4

3 回答 3

17

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

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

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

于 2010-03-23T15:13:51.880 回答
6

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

为了演示这个问题,Equals(str, "")所有字符串都返回 true str,这本质上意味着所有字符串都等于一个空字符串,因此,所有字符串必须具有与空字符串相同的哈希码。因此,正确实现的唯一方法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 实现的效率有多低,并且我不确定是否真的有可能为您的比较规则提供任何有效的实现。

于 2010-03-23T16:23:13.707 回答
1

也许这个问题可以在没有 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);
于 2010-03-24T19:41:12.203 回答