Jeśli chcesz po prostu wiedzieć, czy zestawy są równe, equals
metoda on AbstractSet
jest zaimplementowana mniej więcej tak, jak poniżej:
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Set))
return false;
Collection c = (Collection) o;
if (c.size() != size())
return false;
return containsAll(c);
}
Zwróć uwagę, jak optymalizuje typowe przypadki, w których:
- te dwa obiekty są takie same
- drugi obiekt nie jest w ogóle zbiorem i
- rozmiary obu zestawów są różne.
Po tym containsAll(...)
zwróci, false
gdy tylko znajdzie element w innym zestawie, którego również nie ma w tym zestawie. Ale jeśli wszystkie elementy są obecne w obu zestawach, będzie musiał przetestować je wszystkie.
Dlatego wydajność w najgorszym przypadku występuje, gdy dwa zestawy są równe, ale nie są tymi samymi obiektami. Koszt ten jest zazwyczaj O(N)
lub w O(NlogN)
zależności od implementacji this.containsAll(c)
.
I uzyskuje się wydajność bliską najgorszemu przypadkowi, jeśli zestawy są duże i różnią się tylko niewielkim procentem elementów.
AKTUALIZACJA
Jeśli chcesz zainwestować czas w implementację zestawu niestandardowego, istnieje podejście, które może poprawić „prawie ten sam” przypadek.
Chodzi o to, że musisz wstępnie obliczyć i buforować hash dla całego zestawu, abyś mógł pobrać bieżącą wartość hashcode zestawu O(1)
. Następnie możesz porównać hashcode dla dwóch zestawów jako przyspieszenie.
Jak możesz zaimplementować taki hashcode? Cóż, jeśli ustawiony hashcode to:
- zero dla pustego zestawu i
- XOR wszystkich hashcodes elementu dla niepustego zestawu,
wtedy możesz tanio zaktualizować buforowany hashcode zestawu za każdym razem, gdy dodasz lub usuniesz element. W obu przypadkach po prostu XORujesz kod mieszający elementu z bieżącym ustawionym hashcode.
Oczywiście zakłada się, że hashcodes elementów są stabilne, podczas gdy elementy są członkami zestawów. Zakłada również, że funkcja hashcode klas elementów daje dobry rozkład. Dzieje się tak, ponieważ gdy dwa ustawione hashcodes są takie same, nadal musisz wrócić do O(N)
porównania wszystkich elementów.
Możesz pójść dalej z tym pomysłem ... przynajmniej w teorii.
OSTRZEŻENIE - jest to wysoce spekulacyjne. „Eksperyment myślowy”, jeśli chcesz.
Załóżmy, że twoja klasa elementu set ma metodę zwracania kryptograficznych sum kontrolnych dla elementu. Teraz zaimplementuj sumy kontrolne zestawu, XORując sumy kontrolne zwrócone dla elementów.
Co nam to daje?
Cóż, jeśli założymy, że nic się nie dzieje podstępnie, prawdopodobieństwo, że dowolne dwa nierówne elementy zbioru mają takie same N-bitowe sumy kontrolne, wynosi 2 -N . Prawdopodobieństwo, że 2 nierówne zbiory mają te same N-bitowe sumy kontrolne, również wynosi 2 -N . Więc mój pomysł jest taki, że możesz wdrożyć equals
jako:
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Set))
return false;
Collection c = (Collection) o;
if (c.size() != size())
return false;
return checksums.equals(c.checksums);
}
Zgodnie z powyższymi założeniami, daje to złą odpowiedź tylko raz na 2- N raz. Jeśli zrobisz N wystarczająco duże (np. 512 bitów), prawdopodobieństwo złej odpowiedzi stanie się pomijalne (np. Około 10 -150 ).
Wadą jest to, że obliczanie kryptograficznych sum kontrolnych elementów jest bardzo kosztowne, zwłaszcza gdy rośnie liczba bitów. Więc naprawdę potrzebujesz skutecznego mechanizmu zapamiętywania sum kontrolnych. A to może być problematyczne.
Drugą wadą jest to, że niezerowe prawdopodobieństwo błędu może być niedopuszczalne bez względu na to, jak małe jest to prawdopodobieństwo. (Ale jeśli tak jest ... jak radzić sobie z przypadkiem, w którym promień kosmiczny odwraca krytyczny bit? Lub jeśli jednocześnie odwraca ten sam bit w dwóch przypadkach systemu nadmiarowego?)