2010-03-23 39 views
18

我試圖在兩個字符串列表上實現自定義比較器,並使用.Except()linq方法來獲取那些不是列表中的一個。我做自定義比較器的原因是因爲我需要做一個「模糊」比較,即一個列表中的一個字符串可以嵌入另一個列表中的字符串內。linq除了和自定義的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 obj.GetHashCode(); 
    } 
} 

當我調試,命中是在的GetHashCode()方法唯一的斷點。 Equals()永遠不會被觸及。有任何想法嗎?

+0

對我來說這是一個很好的練習。在我的情況下,我用'public int GetHashCode(string obj){return obj.ToLower()。GetHashCode();}'你的問題是舊的,但我在4年後遇到同樣的問題。 – 2014-02-21 23:00:27

回答

18

如果返回的所有散列碼都不相同,則永遠不需要進行比較。

基本上問題是你的哈希和平等概念是非常不同的。我不完全確定你如何解決這個問題,但在你這樣做之前,肯定無法解決問題。

您需要確保如果Equals(a, b)返回true,那麼GetHashCode(a) == GetHashCode(b)。 (反過來不一定是真的 - 散列衝突是可以接受的,儘管顯然你希望儘可能少的散列衝突。)

+0

我開始認爲這是一種嘗試對預定義對象(即字符串)應用自定義比較的情況。如果我不得不收集自定義對象,那麼我可能會讓它工作。我想我不得不想出一個比這更好的方法。 :(我會留下這個沒有回答的一天,看看是否有其他人有建議 – Joe 2010-03-23 15:42:27

+0

我最終做了兩個步驟,我生成了一個項目的列表,使用一個包含部分匹配,然後轉身除了使用該子集對第一個列表。感謝您的幫助。 – Joe 2010-03-26 11:56:29

5

正如Jon指出的,你需要確保散列碼兩個字符串相等(根據您的比較規則)。這很不幸,相當困難。

爲了說明問題,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實現的效率如何,我不確定實際上是否可以爲比較規則實現任何有效的實現。

1

也許這個問題可以通過IEqualityComparer接口實現來解決。 Jon和Thomas對實現這個接口有很好的觀點,平等似乎並沒有定義你的問題。從你的描述中,我認爲你可以在比較過程中不使用Except擴展名。相反,首先得到比賽,然後做Except。看看這是否適合你:

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);