Kiedy używać produktu porównawczego i porównawczego

108

Mam listę obiektów, które muszę posortować według pola, powiedzmy Wynik. Bez zastanawiania się napisałem nową klasę, która implementuje komparator, który wykonuje zadanie i działa.

Patrząc wstecz na to, zastanawiam się, czy zamiast tego powinienem mieć moją klasę implementującą Comparable, zamiast tworzyć nową klasę, która implementuje Comparator. Wynik jest jedynym polem, w którym obiekty zostaną uporządkowane.

  1. Co zrobiłem do zaakceptowania jako praktyka?

  2. Czy właściwe podejście jest „Najpierw niech klasa implementuje porównywalną (dla naturalnego porządku), a jeśli wymagane jest alternatywne porównanie pól, to utwórz nową klasę, która implementuje komparator”?

  3. Jeśli (2) powyżej jest prawdziwe, to czy oznacza to, że należy wdrożyć komparator dopiero po tym, jak klasa implementuje Comparable? (Zakładając, że jestem właścicielem oryginalnej klasy).

pkrish
źródło

Odpowiedzi:

80

Powiedziałbym, że obiekt powinien implementować opcję Comparable, jeśli jest to naturalny sposób sortowania klasy, a każdy, kto musiałby posortować klasę, na ogół chciałby to zrobić w ten sposób.

Jeśli jednak sortowanie było nietypowym zastosowaniem klasy lub sortowanie ma sens tylko w określonym przypadku użycia, wówczas lepszym rozwiązaniem jest komparator.

Innymi słowy, biorąc pod uwagę nazwę klasy, czy jest jasne, jak będzie sortować porównywalne, czy też musisz uciekać się do czytania javadoc? Jeśli jest to drugie, szanse są takie, że każdy przyszły przypadek sortowania wymagałby komparatora, w którym to momencie wdrożenie porównywalnego może spowolnić użytkowników klasy, a nie przyspieszyć ich.

Yishai
źródło
Czy możesz podać krótki przykład?
rgamber
to może być dobry przykład: gist.github.com/yclian/2627608 Istnieje klasa Version, która używa ComparableVersion. Version - udostępnia metody fabryczne ComparableVersion, które mają być obiektowe (bez metod statycznych) - dostarcza wersję, którą można porównać z inną. Obowiązki są rozdzielone.
sesje
Ankieter zapytał, po co używać Komparatora, skoro to samo można zrobić z Porównywalnym, a ja byłem głupi :(
Aadam,
Link @aLearner nie żyje
G.Brown,
127

Użyj, Comparablejeśli chcesz zdefiniować domyślne (naturalne) zachowanie porządkowe danego obiektu, powszechną praktyką jest użycie do tego technicznego lub naturalnego (bazy danych?) Identyfikatora obiektu.

Użyj, Comparatorjeśli chcesz zdefiniować zewnętrzne zachowanie porządkowania sterowane , może to zastąpić domyślne zachowanie kolejności.

BalusC
źródło
3
To wyjaśnienie techniczne i tak poprawne, jak tylko się da, ale tak naprawdę nie mówi nic o najlepszych praktykach.
extraneon
40
mówi, kiedy użyć każdego z nich - jeśli to nie jest najlepsza praktyka, co to jest?
Bozho
1
„Czy wdrażanie Comparableoznacza, że ​​określam naturalny porządek?” , to dało mi odpowiedź, której szukałem. Dzięki :)
Somjit
61

Zastosowanie Comparable:

  • jeśli obiekt jest pod Twoją kontrolą.
  • jeśli porównywanie jest głównym zachowaniem związanym z porównywaniem.

Zastosowanie Comparator:

  • jeśli obiekt jest poza twoją kontrolą i nie możesz zmusić ich do wykonania Comparable.
  • gdy chcesz porównać zachowanie inne niż domyślne (określone przez Comparable) zachowanie.
Bozho
źródło
20

Porównywalne -java.lang.Comparable: int compareTo(Object o1)

Porównywalny przedmiot jest w stanie porównać się z innym obiektem. Sama klasa musi implementować interfejs java.lang.Comparable, aby móc porównać jej instancje.

  • Możliwość porównywania bieżącego obiektu z podanym obiektem.
  • Korzystając z tego, możemy implementować w only one sort sequenceoparciu o właściwości instancji. DAWNY:Person.id
  • Niektóre z predefiniowanych klas, takich jak String, klasy Wrapper, Date, Calendar mają zaimplementowany interfejs Comparable.

Komparator -java.util.Comparator: int compare(Object o1, Object o2)

Obiekt porównawczy może porównywać dwa różne obiekty. Klasa nie porównuje swoich instancji, ale instancje innych klas. Ta klasa porównawcza musi implementować interfejs java.util.Comparator.

  • Możliwość porównywania dowolnych dwóch obiektów tego samego typu.
  • Korzystając z tego, możemy zaimplementować many sort sequencei nazwać każdy, na podstawie właściwości instancji. DAWNY:Person.id, Person.name, Person.age
  • Możemy zaimplementować interfejs komparatora do naszych wstępnie zdefiniowanych klas w celu niestandardowego sortowania.

Przykład:

public class Employee implements Comparable<Employee> {

    private int id;
    private String name;
    private int age;
    private long salary;

    // Many sort sequences can be created with different names.
    public static Comparator<Employee> NameComparator = new Comparator<Employee>() {         
        @Override
        public int compare(Employee e1, Employee e2) {
            return e1.getName().compareTo(e2.getName());
        }
    };
    public static Comparator<Employee> idComparator = new Comparator<Employee>() {       
        @Override
        public int compare(Employee e1, Employee e2) {
            return Integer.valueOf(e1.getId()).compareTo(Integer.valueOf(e2.getId()));
        }
    };

    public Employee() { }
    public Employee(int id, String name, int age, long salary){
        this.id = id;
        this.name = name;
        this.age = age;
        this.salary = salary;
    }
    // setters and getters.

    // Only one sort sequence can be created with in the class.
    @Override
    public int compareTo(Employee e) {
    //return Integer.valueOf(this.id).compareTo(Integer.valueOf(e.id));
    //return Character.toString(this.name.charAt(0)).compareToIgnoreCase(Character.toString(e.name.charAt(0)));
        if (this.id > e.id) {
            return 1;
        }else if(this.id < e.id){
            return -1;
        }else {
            return Character.toString(this.name.charAt(0)).compareToIgnoreCase(Character.toString(e.name.charAt(0)));
        }

    }   

    public static void main(String[] args) {

        Employee e1 = new Employee(5, "Yash", 22, 1000);
        Employee e2 = new Employee(8, "Tharun", 24, 25000);

        List<Employee> list = new ArrayList<Employee>();
        list.add(e1);
        list.add(e2);
        Collections.sort(list); // call @compareTo(o1)
        Collections.sort(list, Employee.nameComparator); // call @compare (o1,o2)
        Collections.sort(list, Employee.idComparator); // call @compare (o1,o2)
    }
}
  • W celu sortowania niestandardowego wybieramy komparator @compare (o1, o2), dla innych scenariuszy wybieramy porównywalny @compareTo (o1), bez konieczności zmiany kodu, jeśli chcemy posortować więcej niż jedno pole, a następnie używamy komparatora.

W przypadku Java 8 Lambda: Comparator patrz mój post.

Yash
źródło
10

Porównywalne powinno być używane podczas porównywania wystąpień tej samej klasy.

Komparatora można użyć do porównania wystąpień różnych klas.

Porównywalny jest implementowany przez klasę, która musi zdefiniować naturalne uporządkowanie swoich obiektów. Podobnie jak String implementuje porównywalny.

W przypadku, gdy ktoś chce innej kolejności sortowania, może zaimplementować komparator i zdefiniować własny sposób porównywania dwóch instancji.

Devang Paliwal
źródło
10

Jeśli sortowanie obiektów musi opierać się na naturalnym porządku, użyj Porównywalny, a jeśli sortowanie musi być wykonane na atrybutach różnych obiektów, użyj Komparatora w Javie.

Główne różnice między Porównywalnym a Komparatorem:

+------------------------------------------------------------------------------------+
¦               Comparable                ¦                Comparator                ¦
¦-----------------------------------------+------------------------------------------¦
¦ java.lang.Comparable                    ¦ java.util.Comparator                     ¦
¦-----------------------------------------+------------------------------------------¦
¦ int objOne.compareTo(objTwo)            ¦ int compare(objOne, objTwo)              ¦
¦-----------------------------------------+------------------------------------------¦
¦ Negative, if objOne < objTwo            ¦ Same as Comparable                       ¦
¦ Zero,  if objOne == objTwo              ¦                                          ¦
¦ Positive,  if objOne > objTwo           ¦                                          ¦
¦-----------------------------------------+------------------------------------------¦
¦ You must modify the class whose         ¦ You build a class separate from to sort. ¦
¦ instances you want to sort.             ¦ the class whose instances you want       ¦
¦-----------------------------------------+------------------------------------------¦
¦ Only one sort sequence can be created   ¦ Many sort sequences can be created       ¦
¦-----------------------------------------+------------------------------------------¦
¦ Implemented frequently in the API by:   ¦ Meant to be implemented to sort          ¦
¦ String, Wrapper classes, Date, Calendar ¦ instances of third-party classes.        ¦
+------------------------------------------------------------------------------------+
Joby Wilson Mathews
źródło
9

Komparator robi wszystko, co porównywalne, plus więcej.

| | Comparable | Comparator ._______________________________________________________________________________ Is used to allow Collections.sort to work | yes | yes Can compare multiple fields | yes | yes Lives inside the class you’re comparing and serves | | as a “default” way to compare | yes | yes Can live outside the class you’re comparing | no | yes Can have multiple instances with different method names | no | yes Input arguments can be a list of | just Object| Any type Can use enums | no | yes

Znalazłem najlepsze podejście do używania komparatorów jako klas anonimowych w następujący sposób:

private static void sortAccountsByPriority(List<AccountRecord> accounts) {
    Collections.sort(accounts, new Comparator<AccountRecord>() {

        @Override
        public int compare(AccountRecord a1, AccountRecord a2) {
            return a1.getRank().compareTo(a2.getRank());
        }
    });
}

Możesz utworzyć wiele wersji takich metod bezpośrednio w klasie, którą planujesz posortować. Możesz więc mieć:

  • sortAccountsByPriority
  • sortAccountsByType
  • sortAccountsByPriorityAndType

    itp...

Teraz możesz używać tych metod sortowania w dowolnym miejscu i ponownie używać kodu. To daje mi wszystko, co można by porównać, plus więcej ... więc nie widzę żadnego powodu, by używać porównywalnego w ogóle.


źródło
8

Powiedziałbym:

  • jeśli porównanie jest intuicyjne, z całą pewnością zastosuj Porównywalny
  • jeśli nie jest jasne, czy twoje porównanie jest intuicyjne, użyj Komparatora, ponieważ jest on bardziej wyraźny, a tym samym bardziej zrozumiały dla biednej duszy, która musi zachować kod
  • jeśli możliwe jest więcej niż jedno intuicyjne porównanie, wolałbym komparator, prawdopodobnie zbudowany przy użyciu metody fabrycznej w klasie do porównania.
  • jeśli porównanie jest specjalnego przeznaczenia, użyj komparatora
extraneon
źródło
6

Poniższe punkty pomogą Ci zdecydować, w których sytuacjach należy użyć Porównywalnego, a w których Komparator:

1) Dostępność kodu

2) Kryteria sortowania pojedynczego i wielokrotnego

3) Arays.sort () i Collection.sort ()

4) Jako klucze w SortedMap i SortedSet

5) Więcej Liczba klas a elastyczność

6) Porównania międzyklasowe

7) Porządek naturalny

Aby uzyskać bardziej szczegółowy artykuł, możesz zapoznać się z Kiedy używać porównywarki, a kiedy porównywać

uczący się
źródło
Zastanawiam się, dlaczego nikt nie głosuje za tą odpowiedzią. To naprawdę fajne. +1
Diganta,
4
  • Jeśli w momencie pisania klasy masz tylko jeden przypadek użycia sortowania, użyj Porównywalny.
  • Tylko wtedy, gdy masz więcej niż jedną strategię sortowania, zastosuj komparator.
fabrizioM
źródło
4

Jeśli potrzebujesz sortowania w porządku naturalnym - Porównywalne przez użytkownika JEŚLI potrzebujesz sortowania zamówień niestandardowych - użyj komparatora

Przykład:

Class Employee{
private int id;
private String name;
private String department;
}

Sortowanie według porządku naturalnego byłoby oparte na identyfikatorze, ponieważ byłoby unikalne, a sortowanie według niestandardowego porządku obejmowałoby nazwę i dział.

Odniesienia:
Kiedy klasa powinna być porównywalna i / lub porównawcza? http://javarevisited.blogspot.com/2011/06/comparator-and-comparable-in-java.html

Rajiv
źródło
3

Pojawiło się tutaj podobne pytanie: kiedy klasa powinna być porównywalna i / lub porównawcza?

Powiedziałbym tak: Wdrażanie porównywalnego do czegoś w rodzaju naturalnego uporządkowania, np. Na podstawie wewnętrznego identyfikatora

Zaimplementuj komparator, jeśli masz bardziej złożony algorytm porównujący, np. Wiele pól i tak dalej.

ZeissS
źródło
1
Zamawianie na wielu polach może być równie dobre Comparable.
BalusC
Aby poznać
a Learner
2

Porównywalne: zawsze,
gdy chcemy przechowywać tylko jednorodne elementy i wymagany jest domyślny naturalny porządek sortowania, możemy przejść do comparableinterfejsu implementującego klasy .

Komparator:
Zawsze, gdy chcemy przechowywać elementy jednorodne i niejednorodne i chcemy sortować w domyślnej niestandardowej kolejności sortowania, możemy przejść do comparatorinterfejsu.

G swamy reddy
źródło
0

Moje potrzeby były sortowane według daty.

Tak więc użyłem Porównywalnego i to dla mnie z łatwością zadziałało.

public int compareTo(GoogleCalendarBean o) {
    // TODO Auto-generated method stub
    return eventdate.compareTo(o.getEventdate());
}

Jednym z ograniczeń związanych z Porównywalnym jest to, że nie można ich używać do kolekcji innych niż Lista.

wiosna pro
źródło
0

Jeśli posiadasz klasę, lepiej idź z porównywalnym . Ogólnie rzecz biorąc, komparator jest używany, jeśli nie jesteś właścicielem klasy, ale musisz jej użyć jako TreeSet lub TreeMap, ponieważ komparator może zostać przekazany jako parametr w konstruktorze TreeSet lub TreeMap. Możesz zobaczyć, jak korzystać z komparatora i porównywalnego w http://preciselyconcise.com/java/collections/g_comparator.php

Sai Sunder
źródło
0

W jednym z wywiadów poproszono mnie o sortowanie określonego zakresu liczb w czasie lepszym niż nlogn. (Bez sortowania według liczenia)

Zaimplementowanie porównywalnego interfejsu na obiekcie pozwala algorytmom niejawnego sortowania na użycie nadpisanej metody compareTo w celu uporządkowania elementów sortowania i byłby to czas liniowy.

Panie Cudowny
źródło
0

Porównywalny to domyślny naturalny porządek sortowania podany dla wartości liczbowych rosnąco, a dla łańcuchów - kolejności alfabetycznej. na przykład:

Treeset t=new Treeset();
t.add(2);
t.add(1);
System.out.println(t);//[1,2]

Komparator to niestandardowa kolejność sortowania zaimplementowana w niestandardowej klasie myComparator przez zastąpienie metody porównania, np .:

Treeset t=new Treeset(new myComparator());
t.add(55);
t.add(56);
class myComparator implements Comparator{
public int compare(Object o1,Object o2){
//Descending Logic
}
}
System.out.println(t);//[56,55]
Ajay Takur
źródło
-1

Bardzo prostym podejściem jest założenie, że dana klasa encji będzie reprezentowana w bazie danych, a następnie w tabeli bazy danych potrzebny będzie indeks złożony z pól klasy encji? Jeśli odpowiedź brzmi tak, zastosuj porównywanie i użyj pól indeksu do naturalnej kolejności sortowania. We wszystkich innych przypadkach użyj komparatora.

user976715
źródło
-2

Moja biblioteka adnotacji do implementacji Comparablei Comparator:

public class Person implements Comparable<Person> {         
    private String firstName;  
    private String lastName;         
    private int age;         
    private char gentle;         

    @Override         
    @CompaProperties({ @CompaProperty(property = "lastName"),              
        @CompaProperty(property = "age",  order = Order.DSC) })           
    public int compareTo(Person person) {                 
        return Compamatic.doComparasion(this, person);         
    }  
}

Kliknij łącze, aby zobaczyć więcej przykładów. http://code.google.com/p/compamatic/wiki/CompamaticByExamples

Jianmin Liu
źródło