2012-10-06 13 views
8

Napisałem kod here, który poprawnie rozwiązał problem z plakatem. OP chciał usunąć duplikaty i wnieść pewne specjalne przedmioty na szczyt listy. Użyłem klasy TreeSet ze specjalną klasą Comparable, która owijała Locale, z którą pracowali, aby osiągnąć to, co chcieli.Równa się i porównywalna z zestawami

Potem dostał się do myślenia ... jak ty ... że ja eliminowanie duplikatów wracając 0 od sposobu compareTo, nie powracając true od implementacji equals jako jeden musiałby zrobić, aby poprawnie wskazywać duplikat w Set (od definition z Set).

Nie mam zastrzeżeń do stosowania tej techniki, ale czy używam tego, co może być uznane za nieudokumentowaną funkcję? Czy mogę bezpiecznie założyć, że robienie tego rodzaju rzeczy będzie dalej działało?

+0

Jak Pan Nurkiewicz zwraca uwagę, jest to określone zachowanie, więc jest to bezpieczne. Zgadzam się, ale to zaskakujące! –

Odpowiedz

17

Wydaje się to dość dobrze udokumentowane w JavaDoc of TreeSet (wytłuszczenie moje):

Zauważ, że kolejność utrzymywane przez zestaw (czy wyraźne komparator jest) muszą być zgodne z równa jeśli ma to poprawnie implementować interfejs Set. (Patrz Comparable lub Comparator dla precyzyjnego zdefiniowania zgodne z równymi). Jest tak, ponieważ interfejs Set jest określona w odniesieniu do operacji equals, ale TreeSet przykład wykonuje wszystkie porównania elementów za pomocą jego compareTo (lub porównać) metoda, więc dwa elementy, które są uznawane za równe tą metodą, są z punktu widzenia zbioru równe. Zachowanie zestawu jest dobrze zdefiniowane, nawet jeśli jego kolejność jest niespójna z równymi; to po prostu nie jest zgodne z ogólną umową interfejsu Set.

Oto przykład z only (?) JDK class który implementuje Comparable ale nie jest zgodne z equals():

Set<BigDecimal> decimals = new HashSet<BigDecimal>(); 
decimals.add(new BigDecimal("42")); 
decimals.add(new BigDecimal("42.0")); 
decimals.add(new BigDecimal("42.00")); 
System.out.println(decimals); 

decimals na końcu mieć trzy wartości ponieważ 42, 42.0 i 42.00 nie są równe miarę equals() jest zaniepokojony. Ale jeśli zastąpisz HashSet przez TreeSet, wynikowy zestaw zawiera tylko 1 element (42 - który był pierwszym dodanym), ponieważ wszystkie z nich są uważane za równe w porównaniu z użyciem BigDecimal.compareTo().

Pokazuje to, że TreeSet jest w pewnym sensie "zepsuty" w przypadku używania typów niezgodnych z equals(). Nadal działa poprawnie i wszystkie operacje są dobrze zdefiniowane - po prostu nie przestrzega umowy z klasy Set - jeśli dwie klasy nie są equal(), nie są uważane za duplikaty.

Zobacz także

+0

Interesujące, że takiego komentarza nie ma w dokumentach "ConcurrentSkipListSet". – OldCurmudgeon

+1

@ OldCurmudgeon Jest on również określony w [javadoc z SortedSet] (http://docs.oracle.com/javase/7/docs/api/java/util/SortedSet.html) (i obu narzędziach TreeSet i ConcurrentSkipListSet). berło). – assylias

+0

Tak to robi !! Dobry połów. – OldCurmudgeon