2015-10-13 1 views
3

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

Исходя из этого предположения (documentation):

В общем, для изменяемых ссылочных типов, вы должны переопределить GetHashCode только если:

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

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

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

РЕШЕНИЕ 1

Игнорировать проблему. Подсчитайте, если с одним из доступных алгоритмов (здесь и, например, для гео координат в C#):

public override Int32 GetHashCode() { 
    Int32 n1 = 99999997; 
    Int32 hash_lat = this.Latitude.GetHashCode() % n1; 
    Int32 hash_lng = this.Longitude.GetHashCode(); 
    _final_hashcode = (((hash_lat << 5) + hash_lat)^hash_lng); 
    return _final_hashcode.Value; 
} 

РЕШЕНИЕ 2

Вычислить его в первый раз на изменяемых значений и хранить его в очередной раз :

private Int32? _final_hashcode = null; 
public override Int32 GetHashCode() { 
    // hash code must not change when lat and lng does change 
    if (_final_hashcode == null) { 
     Int32 n1 = 99999997; 
     Int32 hash_lat = this.Latitude.GetHashCode() % n1; 
     Int32 hash_lng = this.Longitude.GetHashCode(); 
     _final_hashcode = (((hash_lat << 5) + hash_lat)^hash_lng); 
    } 
    return _final_hashcode.Value; 
} 

РЕШЕНИЕ 3

Добавить закрытый неизменный ключ для объекта, который будет использоваться только для хэш-кода. Таким образом, когда изменяемые поля изменяются, хеш-код не изменяется.

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

public class GeoPosition { 

    private const Guid _guidForHash = Guid.NewGuid(); // init during contruction 

    public override Int32 GetHashCode() { 
     return _guidForHash.GetHashCode(); 
    } 

    // mutable properties here and other stuff 
    // ... 
} 

Что вы думаете?

+0

Я не знаю всех соответствующих критериев в суждении между решениями, - но третий делает меня чище, даже если у него немного накладных расходов. –

+0

Это звучит как XY-проблема для меня. У всех ваших решений есть проблемы. Для решения 1 это очевидно (вы сами написали). Для решений 2 и 3 два объекта, которые имеют одни и те же данные, могут приводить к различным хэш-кодам, в зависимости от того, когда сначала был вычислен хэш-код. Итак: вам нужно лучше описать, что такое ваша проблема _real_. –

+0

@ThomasMueller Вы правы. Во всяком случае, где вы получили информацию о том, что проблема с двумя объектами, имеющими одни и те же данные, и разные хэш-коды? Является ли требование вычисления хэш-кода, или что? –

ответ

0

Это очень просто:

  • РЕШЕНИЕ 2: Если у вас есть объекты O1 и O2, и они имеют разные значения полей, они имеют разные хэш-коды. Если вы затем измените поля этих объектов, чтобы они были равны друг другу, они все равно не будут иметь один и тот же хеш-код. Это нарушает ограничение: o1 == o2 implies hash(o1) == hash(o2). Нерабочее решение.

  • РЕШЕНИЕ 3: Та же проблема, как 2.

  • РЕШЕНИЕ 1: А правильный хэш-функции, но требует перерасчета хэш-код каждый раз.

Так РЕШЕНИЕ 1 это. Если вам нужно его оптимизировать (помните, что преждевременная оптимизация - это корень всего зла), вы можете кэшировать хэш-код и обновлять его после каждого атрибута:

private Int32 UpdateHashCode() { 
     Int32 n1 = 99999997; 
     Int32 hash_lat = this.Latitude.GetHashCode() % n1; 
     Int32 hash_lng = this.Longitude.GetHashCode(); 
     cached_hashcode = (((hash_lat << 5) + hash_lat)^hash_lng); 
} 
private Int32 cached_hashcode = null; 
public override Int32 GetHashCode() { 
    if (cached_hashcode == null) { 
     UpdateHashCode(); 
    } 
    return cached_hashcode.Value; 
} 
private string latitude; 
public string Latitude { 
    set { 
     latitude = value; 
     UpdateHashCode(); 
    } 
} 
private string longitude; 
public string Longitude { 
    set { 
     longitude = value; 
     UpdateHashCode(); 
    } 
} 
+1

Tnx для вашего решения, но как вы решаете тот факт, что «хэш-код изменяемого объекта не должен меняться, пока объект содержится в коллекции, которая полагается на свой хэш-код»? –

Смежные вопросы