Должен ли я использовать конкатенацию моих строковых полей в качестве хеш-кода? - PullRequest
14 голосов
/ 05 июня 2009

У меня есть класс Address в C #, который выглядит так:

public class Address
{            
    public string StreetAddress { get; set; }
    public string RuralRoute { get; set; }
    public string City { get; set; }
    public string Province { get; set; }
    public string Country { get; set; }
    public string PostalCode { get; set; }
}

Я реализую равенство, поэтому мне нужно переопределить хэш-код. Сначала я собирался использовать формулу хэш-кода из EJ, но потом подумал: это все строковые поля, не могу ли я просто использовать StringBuilder для их объединения и возврата хеш-кода из этой строки?

То есть:

var str = new StringBuilder();
str.Append(StreetAddress)
   .Append(RuralRoute)
   ...

return str.ToString().GetHashCode();

Каковы преимущества / недостатки этого? Почему я не должен это делать?

Ответы [ 4 ]

15 голосов
/ 05 июня 2009

Я бы не стал делать это просто на том основании, что это создает бессмысленную кучу строк - хотя пункт Kosi2801 о том, как сделать столкновения простыми, также актуален. (Я подозреваю, что из-за природы полей на самом деле не создаст много коллизий, но ...)

Я бы пошел по «простому и простому получению правильного» алгоритма, который я ранее использовал в этом ответе (спасибо, что нашел его, lance :) - и который указан в Effective Java, как вы сказали. В этом случае это будет выглядеть так:

public int GetHashCode()
{
    int hash = 17;
    // Suitable nullity checks etc, of course :)
    hash = hash * 23 + StreetAddress.GetHashCode();
    hash = hash * 23 + RuralRoute.GetHashCode();
    hash = hash * 23 + City.GetHashCode();
    hash = hash * 23 + Province.GetHashCode();
    hash = hash * 23 + Country.GetHashCode();
    hash = hash * 23 + PostalCode.GetHashCode();
    return hash;
}

Конечно, это небезопасно. Если вы используете C # 3, вы можете рассмотреть метод расширения:

public static int GetNullSafeHashCode<T>(this T value) where T : class
{
    return value == null ? 1 : value.GetHashCode();
}

Тогда вы можете использовать:

public int GetHashCode()
{
    int hash = 17;
    // Suitable nullity checks etc, of course :)
    hash = hash * 23 + StreetAddress.GetNullSafeHashCode();
    hash = hash * 23 + RuralRoute.GetNullSafeHashCode();
    hash = hash * 23 + City.GetNullSafeHashCode();
    hash = hash * 23 + Province.GetNullSafeHashCode();
    hash = hash * 23 + Country.GetNullSafeHashCode();
    hash = hash * 23 + PostalCode.GetNullSafeHashCode();
    return hash;
}

Вы можете создать утилиту для метода массива параметров, чтобы сделать это еще проще:

public static int GetHashCode(params object[] values)
{
    int hash = 17;
    foreach (object value in values)
    {
        hash = hash * 23 + value.GetNullSafeHashCode();
    }
    return hash;
}

и позвоните по этому номеру:

public int GetHashCode()
{
    return HashHelpers.GetHashCode(StreetAddress, RuralRoute, City,
                                   Province, Country, PostalCode);
}

В большинстве типов задействованы примитивы, поэтому в некоторых случаях выполнять упаковку излишне, но в этом случае у вас будут только ссылки. Конечно, вы в конечном итоге создадите массив без необходимости, но вы знаете, что они говорят о преждевременной оптимизации ...

5 голосов
/ 05 июня 2009

Не делайте этого, потому что объекты могут отличаться, хотя хеш-код один и тот же.

Подумайте о

"StreetAddress" + "RuralRoute" + "City"

против

"Street" + "AddressRural" + "RouteCity"

Оба будут иметь одинаковый хеш-код, но разное содержимое в полях.

0 голосов
/ 07 июня 2015
public string getfourDigitEncryptedText(string input) { 
    int hashCode = input.hashCode(); 
    string hstring = (new StringBuilder()).append(hashCode).append("").toString(); 
    string rev_hstring = (new StringBuilder(hstring)).reverse().toString(); 
    string parts[] = rev_hstring.trim().split(""); 
    int prefixint = 0; 
    for(int i = 1; i <= parts.length - 3; i++) 
        prefixint += integer.parseInt(parts[i]);   
    string prefixstr = "0"; 
    if((new integer(prefixint)).toString().length() < 2) 
        prefixstr = (new StringBuilder()).append((new integer(prefixint)).toString()).append("5").toString(); 
    else if((new integer(prefixint)).toString().length() > 2) 
        prefixstr = "79"; 
    else 
        prefixstr = (new integer(prefixint)).toString(); 
    string finalstr = (new StringBuilder()).append(prefixint).append(rev_hstring.substring(3, 5)).toString();
    return finalstr; 
}  
0 голосов
/ 05 июня 2009

Для такого рода вещей вы можете реализовать IEqualityComparer<Address>:

public class Address : IEqualityComparer<Address>
{        
    //
    // member declarations
    //

    bool IEqualityComparer<Address>.Equals(Address x, Address y)
    {
        // implementation here
    }

    int IEqualityComparer<Address>.GetHashCode(Item obj)
    {
        // implementation here
    }
}

Вы также можете реализовать IComparable<Address>, чтобы получить заказ ...

...