2010-05-26 18 views
5

Ja używałem Budowniczy apache hashcode dużoC# hashcode Builder

Czy ta istnieje dla C#

+1

C# Implementacje Szmery i XXHash tutaj. http://geteventstore.com/blog/?p=36 –

Odpowiedz

3

używam następujące:

public static int ComputeHashFrom(params object[] obj) { 
    ulong res = 0; 
    for(uint i=0;i<obj.Length;i++) { 
     object val = obj[i]; 
     res += val == null ? i : (ulong)val.GetHashCode() * (1 + 2 * i); 
    } 
    return (int)(uint)(res^(res >> 32)); 
} 

Stosując taki pomocnik jest szybkie, łatwe i niezawodny, ale ma potencjalne dwa wady (których prawdopodobnie nie spotkasz często, ale dobrze o tym pamiętać):

  • Może generować słabe hashcode dla niektórych dystrybucji parametrów. Na przykład dla każdego int x, ComputeHashFrom(x*-3, x) == 0 - więc jeśli twoje obiekty mają pewne właściwości patologiczne, możesz uzyskać wiele kolizji kodu haszowania, co skutkuje słabymi słownikami i hasłami. Prawdopodobnie tak się nie stanie, ale obliczenia kodu skrótu uwzględniające typ mogą łatwiej uniknąć takich problemów.
  • Obliczanie kodu skrótu jest wolniejsze niż może być wyspecjalizowane obliczenie. W szczególności chodziło o przydzielenie macierzy params i pętli - co jest dość niepotrzebnym obciążeniem, jeśli masz tylko dwóch członków do przetworzenia.

Żadna z wad nie powoduje błędów, a jedynie nieskuteczność; i oba pojawiają się w profilerze jako blipy w tej metodzie lub w wewnętrznych elementach konsumenta kodu skrótu.

+0

Do twojego zastrzeżenia dotyczącego szybkości dodałem, że możesz również produkować lepsze kody skrótów za pomocą metody świadomej typu, szczególnie jeśli masz rozsądne pojęcie wartości będą się zdarzać najczęściej. Skłoniłabym się do tego, żeby więcej o tym myśleć, niż o szybkość obliczeń. –

+0

Punktem takiej prostej i bezpiecznej metody jest być prosty i bezpieczny. W przypadku niektórych dystrybucji obiektów spowoduje to gorsze hashkody, a obliczenie będzie wymagało nieco więcej czasu. Jednak w ogólnym przypadku, te hashcody działają dobrze (w końcu członkowie obj są generalnie świadomymi typu implementacjami GetHashCode), a większość programów nie spędza większości czasu na tworzeniu lub używaniu kodów hash. Z praktycznego doświadczenia jednak zauważę, że wpadłem na problemy z perfekcją GetHashCode, ale nie napotkaliśmy problemów z jakością * tej * implementacji - YMMV. –

+0

W praktyce, ponieważ porównanie równości obiektów jest często znacznie tańsze niż ".GetHashCode", w ostateczności płacisz niewiele, jeśli napotkasz kilka * więcej kolizji. Z drugiej strony, jeśli wykonujesz wiele obliczeń set/dictionary, możesz po prostu buforować hashcode niezmienionych obiektów; ale nie można uniknąć konsekwencji złego kodu hash. W każdym razie, w praktyce nie zawracałbym sobie głowy niczym bardziej skomplikowanym, dopóki profilowanie nie ujawni, że warto, a prawie nigdy tak nie jest. –

1

C# nie ma wbudowanego konstruktora HashCode, ale można przetasować własny, ostatnio miałem dokładnie ten problem i stworzyłem generator haseł, który nie używa boksu, używając generycznych i implementuje zmodyfikowany FNV (Fowler/Noll/Vo Hash) do generowania specyficznego mieszania, ale można użyć innego dowolnego algorytmu, który chcesz, jak jeden z algorytmów w System.Security.Cryptography

public static int GetHashCode<T>(params T[] args) 
    { 
     return args.GetArrayHashCode(); 
    } 

    public static int GetArrayHashCode<T>(this T[] objects) 
    { 
     int[] data = new int[objects.Length]; 

     for (int i = 0; i < objects.Length; i++) 
     { 
      T obj = objects[i]; 
      data[i] = obj == null ? 1 : obj.GetHashCode(); 
     } 

     return GetFnvHash(data); 
    } 

    private static int GetFnvHash(int[] data) 
    { 
     unchecked 
     { 
      const int p = 16777619; 
      long hash = 2166136261; 

      for (int i = 0; i < data.Length; i++) 
      { 
       hash = (hash^data[i]) * p; 
      } 

      hash += hash << 13; 
      hash ^= hash >> 7; 
      hash += hash << 3; 
      hash ^= hash >> 17; 
      hash += hash << 5; 

      return (int)hash; 
     } 
    } 
2

to mój domowy konstruktor .

Zastosowanie:

hash = new HashCodeBuilder(). 
      Add(a). 
      Add(b). 
      Add(c). 
      Add(d). 
      GetHashCode(); 

Nie ma znaczenia, jaki rodzaj pola a, b, c i d są, łatwo rozszerzyć, nie ma potrzeby, aby utworzyć tablicę.

Źródło:

public sealed class HashCodeBuilder 
{ 
    private int hash = 17; 

    public HashCodeBuilder Add(int value) 
    { 
     unchecked 
     { 
      hash = hash * 31 + value; //see Effective Java for reasoning 
      // can be any prime but hash * 31 can be opimised by VM to hash << 5 - hash 
     } 
     return this; 
    } 

    public HashCodeBuilder Add(object value) 
    { 
     return Add(value != null ? value.GetHashCode() : 0); 
    } 

    public HashCodeBuilder Add(float value) 
    { 
     return Add(value.GetHashCode()); 
    } 

    public HashCodeBuilder Add(double value) 
    { 
     return Add(value.GetHashCode()); 
    } 

    public override int GetHashCode() 
    { 
     return hash; 
    } 
} 

wykorzystanie próbki:

public sealed class Point 
{ 
    private readonly int _x; 
    private readonly int _y; 
    private readonly int _hash; 

    public Point(int x, int y) 
    { 
     _x = x; 
     _y = y; 
     _hash = new HashCodeBuilder(). 
      Add(_x). 
      Add(_y). 
      GetHashCode(); 
    } 

    public int X 
    { 
     get { return _x; } 
    } 

    public int Y 
    { 
     get { return _y; } 
    } 

    public override bool Equals(object obj) 
    { 
     return Equals(obj as Point); 
    } 

    public bool Equals(Point other) 
    { 
     if (other == null) return false; 
     return (other._x == _x) && (other._y == _y); 
    } 

    public override int GetHashCode() 
    { 
     return _hash; 
    } 
} 
+0

Podoba mi się koncepcja hermetyzacji takiego kodu wielokrotnego użytku. Ale czy nie ma kary za wydajność spowodowanej wywołaniem metody? –

+1

@SteveB Zależy od sposobu użycia. Biorąc pod uwagę, powinieneś optymalnie używać tylko niezmiennych danych w hashach, jeśli robisz to raz w konstruktorze, a następnie przechowujesz wynik w prywatnym hash '' '' ', jest bardziej efektywny niż normalne obliczanie za każdym razem, gdy wywoływany jest' GetHashCode'. – weston

+0

@SteveB Dodano przykład użycia, aby pokazać, co mam na myśli. Ma również poprawną implementację równości. – weston