2015-06-09 11 views
7

Obecnie pracuję nad biblioteką kolekcji dla mojego niestandardowego języka programowania. Mam już kilka typów danych (Kolekcja, Lista, Mapa, Zestaw) i implementacje dla nich (zmienne i niezmienne), ale dotychczas brakowało mi hashCode i equals. Chociaż nie są one problemem dla list, ponieważ są one uporządkowanymi kolekcjami, odgrywają szczególną rolę dla zestawów i map. Dwa zestawy są uważane za równe, jeśli mają ten sam rozmiar i te same elementy, a kolejność, w jakiej zestawy je zachowują, nie powinna mieć wpływu na ich równość. Z powodu równości-hashCode-kontrakt implementacja hashCode musi również odzwierciedlać to zachowanie, co oznacza, że ​​dwa zestawy z tymi samymi elementami, ale z inną kolejnością, powinny mieć ten sam kod skrótu. (To samo dotyczy Maps, które są technicznie Zestaw klucz-wartość-parach)Niezależny od zamówienia algorytm skrótu

przykładu (Pseudokod):

let set1: Set<String> = [ "a", "b", "c" ] 
let set2: Set<String> = [ "b", "c", "a" ] 
set1 == set2  // should return true 
set1.hashCode == set2.hashCode // should also return true 

Jak zaimplementować całkiem dobry algorytm mieszania dla których hashCode s w powyższym przykładzie zwraca tę samą wartość?

+0

Jak o parze (suma, produktu) warunków w zestawie? Oba razem nie byłyby wspólne dla różnych zestawów liczb (o ile widziałem). –

+0

Na przykład coś podobnego do '(e1.hashCode() + e2.hashCode() + ... + en.hashCode())^(e1.hashCode() * e2.hashCode() * ... * en.hashCode()) '? – Clashsoft

+1

Czy próbowałeś sprawdzić, w jaki sposób Java to implementuje? – RealSkeptic

Odpowiedz

4

Sam JDK proponuje następujące rozwiązanie tego problemu. Umowa interfejsu java.util.Set określa:

Powoduje zwrócenie wartości skrótu dla tego zestawu. Kod skrótu zestawu jest zdefiniowany jako suma kodów skrótu elementów w zestawie, gdzie kod skrótu elementu zerowego jest zdefiniowany jako zero. Zapewnia to, że s1.equals (s2) implikuje, że s1.hashCode() == s2.hashCode() dla dowolnych dwóch zestawów s1 i s2, zgodnie z wymaganiami generalnej umowy Object.hashCode().

Alternatywą dla wykorzystania sumy kodów hash wpisów jest użycie na przykład operatora ^ (XOR).

język Scala używa zamawiania wersji niezmienny algorytmu na Murmurhash (por prywatnej scala.util.hashing.MurmurHash3 klasy) w celu wdrożenia metody hashCode (lub ##) jego immutable sets i podobnych zbiorów.

+0

Jak już stwierdziłem w komentarzach, znalazłem już rozwiązanie JDK dla tego problemu, ale chcę wiedzieć o bardziej użytecznym nieuporządkowanym algorytmie mieszania kolekcji o mniejszym potencjale kolizyjnym. – Clashsoft

+0

@Clashsoft Jaki potencjał kolizji? Jeśli tylko jeden z indywidualnych kodów skrótu działa dobrze, cały algorytm mieszania będzie równomiernie rozłożony. – btilly

+0

@btilly [Rozkład sumy jednolitych zmiennych losowych] (https://en.wikipedia.org/wiki/Irwin%E2%80%93Hall_distribution) nie jest jednolity! – augurar

0

Oto pseudokod do ewentualnej realizacji:

String hashCode = null; 
for(element : elements){ 
    hashCode = xor(hashCode, getHashCode(element)); 
} 
return hashCode; 

Funkcja xor powinien zwrócić ciąg znaków, który jest tak długo, jak najdłużej z dwoma argumentami. Będzie XOR bity w każdym, aż dojdzie do końca jednego z argumentów. Następnie pobiera pozostałe bity z dłuższego łańcucha i dołącza je.

Ta implementacja będzie oznaczać, że hashCode zestawu będzie tak długi, jak hashCode jego najdłuższego elementu. Ponieważ wyszukujesz bity, na końcu hashcode będzie taki sam niezależnie od kolejności twoich elementów. Jednak, podobnie jak w przypadku każdej implementacji mieszania, będzie możliwość kolizji.

+0

Ale co zrobić z "String", gdy potrzebuję 'hashChats' int? To wydaje się bardzo pomysłowym rozwiązaniem. – Clashsoft

+0

@Clashsoft Nie byłem pewien, czy chcesz 'int' lub' String'. Jeśli jest to tylko int, to pobranie sumy hashCodes poszczególnych elementów da ci to, czego potrzebujesz, o ile przepełnienia zawijają się zamiast powodować błędy. Jeśli przepełnienia powodują błędy, musisz zająć się tym przypadkiem jawnie i zawijać ręcznie. Ta sama koncepcja. – Briguy37

+0

Dziękuję za odpowiedź, ale chcę znaleźć inne rozwiązanie oprócz sumowania kodów skrótów elementów (patrz komentarze). – Clashsoft

1

Możesz obliczyć sumę kontrolną, sortując swoją kolekcję w kolejności alfabetycznej.

Nie jest próbka C# - Mam nadzieję, że można go przetłumaczyć w Javie :)

static String GetHash(List<String> l) 
{ 
    using (System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create()) 
    { 
     return BitConverter.ToString(md5.ComputeHash(l.OrderBy(p => p).SelectMany(s => System.Text.Encoding.ASCII.GetBytes(s + (char)0)).ToArray())).Replace("-", ""); 
    } 
}