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).

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.


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.


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.

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.


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.


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.        ¦
+------------------------------------------------------------------------------------+

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.


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

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ć


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.

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



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.


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.


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


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.


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]

-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.


-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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.