Próbuję zoptymalizować fragment kodu, który porównuje elementy listy.
Na przykład.
public void compare(Set<Record> firstSet, Set<Record> secondSet){
for(Record firstRecord : firstSet){
for(Record secondRecord : secondSet){
// comparing logic
}
}
}
Proszę wziąć pod uwagę, że ilość rekordów w zestawach będzie duża.
Dzięki
Shekhar
java
performance
set
Shekhar
źródło
źródło
Odpowiedzi:
To naprawdę zależy od tego, co chcesz zrobić w logice porównania… czyli co się stanie, jeśli znajdziesz element w jednym zestawie, a nie w drugim? Twoja metoda ma
void
typ zwrotu, więc zakładam, że wykonasz niezbędną pracę w tej metodzie.Bardziej precyzyjna kontrola, jeśli jej potrzebujesz:
Jeśli potrzebujesz zdobyć elementy, które są w jednym zestawie, a nie w drugim.
EDYCJA:
set.removeAll(otherSet)
zwraca wartość logiczną, a nie zestaw. Aby użyć removeAll (), musisz skopiować zestaw, a następnie go użyć.Jeśli zawartość
one
itwo
oba są puste, to wiesz, że oba zestawy były równe. Jeśli nie, to masz elementy, które sprawiły, że zestawy były nierówne.Wspomniałeś, że liczba rekordów może być wysoka. Jeśli podstawową implementacją jest
HashSet
a, pobieranie każdego rekordu odbywa się naO(1)
czas, więc naprawdę nie można uzyskać nic lepszego.TreeSet
jestO(log n)
.źródło
equals
jest szybsza niż dwa wywołaniacontainsAll
w najgorszym przypadku; zobacz moją odpowiedź.Jeśli chcesz po prostu wiedzieć, czy zestawy są równe,
equals
metoda onAbstractSet
jest zaimplementowana mniej więcej tak, jak poniżej:Zwróć uwagę, jak optymalizuje typowe przypadki, w których:
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 wO(NlogN)
zależności od implementacjithis.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:
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: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?)
źródło
W guawie istnieje metoda,
Sets
która może tutaj pomóc:źródło
Masz następujące rozwiązanie z https://www.mkyong.com/java/java-how-to-compare-two-sets/
Lub jeśli wolisz użyć pojedynczej instrukcji powrotu:
źródło
equals()
metody zAbstractSet
(dostarczonej z JDK), która jest prawie taka sama jak tutaj rozwiązanie, z wyjątkiem dodatkowych sprawdzeń zerowych . Interfejs zestawu Java-11Istnieje rozwiązanie O (N) dla bardzo specyficznych przypadków, w których:
Poniższy kod zakłada, że oba zestawy są oparte na porównywalnych rekordach. Podobna metoda mogłaby opierać się na komparatorze.
źródło
Jeśli korzystasz z
Guava
biblioteki, możesz:A następnie wyciągnij wnioski na tej podstawie.
źródło
Przed porównaniem umieściłbym secondSet w HashMap. W ten sposób zredukujesz czas przeszukiwania drugiej listy do n (1). Lubię to:
źródło
źródło
Myślę, że można użyć odwołania do metody z metodą równości. Zakładamy, że typ obiektu bez cienia wątpliwości ma własną metodę porównania. Oto jasny i prosty przykład,
źródło
set.equals(set2)