Come confrontare oggetti per più campi


237

Supponiamo di avere alcuni oggetti che hanno diversi campi che possono essere confrontati con:

public class Person {

    private String firstName;
    private String lastName;
    private String age;

    /* Constructors */

    /* Methods */

}

Quindi, in questo esempio, quando chiedi se:

a.compareTo(b) > 0

potresti chiederti se il cognome di a viene prima di b, o se a è più vecchio di b, ecc ...

Qual è il modo più pulito per consentire il confronto multiplo tra questo tipo di oggetti senza aggiungere ingombro o sovraccarico inutili?

  • java.lang.Comparable l'interfaccia consente il confronto solo per un campo
  • L'aggiunta di numerosi metodi di confronto (ad es compareByFirstName(). compareByAge(), Ecc.) È ingombra secondo me.

Qual è il modo migliore per farlo?


3
perché questo è un cw? È una domanda di programmazione perfettamente valida.
Elie,

2
Sei a conoscenza del fatto che Comparable consente il confronto di tutti i campi che desideri?
DJClayworth,

Risposte:


81

Puoi implementare un oggetto Comparatorche confronta due Personoggetti e puoi esaminare tutti i campi che desideri. Puoi inserire una variabile nel tuo comparatore che gli dica in quale campo confrontare, anche se probabilmente sarebbe più semplice scrivere più comparatori.


5
In realtà preferisco l'idea di utilizzare un singolo comparatore. Non penso che questa risposta sia sbagliata, ma chiunque la legga dovrebbe assolutamente controllare la risposta di Steve Kuo di seguito.
Felipe Leão,

I comparatori multipli erano solo se si desideravano metodi di confronto diversi che non sono una funzione dei dati stessi - cioè a volte si desidera confrontare per nome, altre volte per età, ecc. Per confrontare per più campi contemporaneamente, solo un comparatore sarebbe necessario.
Elie,

399

Con Java 8:

Comparator.comparing((Person p)->p.firstName)
          .thenComparing(p->p.lastName)
          .thenComparingInt(p->p.age);

Se hai metodi di accesso:

Comparator.comparing(Person::getFirstName)
          .thenComparing(Person::getLastName)
          .thenComparingInt(Person::getAge);

Se una classe implementa Comparable, tale comparatore può essere utilizzato nel metodo compareTo:

@Override
public int compareTo(Person o){
    return Comparator.comparing(Person::getFirstName)
              .thenComparing(Person::getLastName)
              .thenComparingInt(Person::getAge)
              .compare(this, o);
}

5
Soprattutto il cast (Person p)è importante per i comparatori concatenati.
membri del

5
Quanto è efficiente quando si confronta un numero elevato di oggetti, ad esempio durante l'ordinamento? Deve creare nuove Comparatoristanze in ogni chiamata?
jjurm,

4
Ottengo una NullPointerException quando uno dei campi che sto confrontando è nullo, come una stringa. Esiste un modo per mantenere questo formato di confronto ma consentirne la null nullità?
Raggiungi

3
@jjurm .thenComparing(Person::getLastName, Comparator.nullsFirst(Comparator.naturalOrder()))- primo selettore di campo, poi comparatore
gavenkoa,

2
@jjurm quando lo usi compareTocome mostrato sopra, Comparatorviene creato ogni volta che viene chiamato il metodo. È possibile impedire ciò memorizzando il comparatore in un campo finale statico privato.
Gandalf,

165

Dovresti implementare Comparable <Person>. Supponendo che tutti i campi non saranno nulli (per semplicità), che age sia un int e che confrontare la classifica sia prima, ultima, age, il compareTometodo è abbastanza semplice:

public int compareTo(Person other) {
    int i = firstName.compareTo(other.firstName);
    if (i != 0) return i;

    i = lastName.compareTo(other.lastName);
    if (i != 0) return i;

    return Integer.compare(age, other.age);
}

10
se si implementa Comparable <Person>, il metodo è compareTo (Person p) .. sembra che questa risposta sia stata confusa con il metodo Comparator <T o1, T o2>
Mike

5
Questo non è raccomandato Usa Comparator quando hai più campi.
indika,

1
questa è la soluzione migliore al momento, (meglio di più comparatori)
Vasile Surdu,

4
@indika, sono curioso: perché questo non è raccomandato? Il confronto con più di una proprietà mi sembra perfettamente perfetto.
ars-longa-vita-brevis,

4
@ ars-longa-vita-brevis, Se si utilizza Comparable, la logica di ordinamento deve appartenere alla stessa classe i cui oggetti vengono ordinati in modo che questo sia chiamato ordinamento naturale degli oggetti. Con l'uso di Comparator è possibile scrivere una logica di ordinamento personalizzata al di fuori della classe Person. Se si desidera confrontare gli oggetti Person solo con il suo nome o cognome, non è possibile utilizzare questa logica. Devi scriverlo di nuovo,
indika,

78

(da Ways per ordinare gli elenchi di oggetti in Java in base a più campi )

Codice di lavoro in questo senso

Utilizzo di Java 8 lambda (aggiunto il 10 aprile 2019)

Java 8 risolve questo problema con Lambda (anche se Guava e Apache Commons potrebbero offrire ancora maggiore flessibilità):

Collections.sort(reportList, Comparator.comparing(Report::getReportKey)
            .thenComparing(Report::getStudentNumber)
            .thenComparing(Report::getSchool));

Grazie alla risposta di @ gaoagong di seguito .

Disordinato e contorto: ordinamento a mano

Collections.sort(pizzas, new Comparator<Pizza>() {  
    @Override  
    public int compare(Pizza p1, Pizza p2) {  
        int sizeCmp = p1.size.compareTo(p2.size);  
        if (sizeCmp != 0) {  
            return sizeCmp;  
        }  
        int nrOfToppingsCmp = p1.nrOfToppings.compareTo(p2.nrOfToppings);  
        if (nrOfToppingsCmp != 0) {  
            return nrOfToppingsCmp;  
        }  
        return p1.name.compareTo(p2.name);  
    }  
});  

Ciò richiede molta digitazione, manutenzione ed è soggetto a errori.

Il modo riflessivo: ordinare con BeanComparator

ComparatorChain chain = new ComparatorChain(Arrays.asList(
   new BeanComparator("size"), 
   new BeanComparator("nrOfToppings"), 
   new BeanComparator("name")));

Collections.sort(pizzas, chain);  

Ovviamente questo è più conciso, ma ancora più soggetto a errori man mano che perdi il tuo riferimento diretto ai campi usando invece Stringhe (nessuna sicurezza tipografica, auto-refactoring). Ora se un campo viene rinominato, il compilatore non segnalerà nemmeno un problema. Inoltre, poiché questa soluzione utilizza la riflessione, l'ordinamento è molto più lento.

Come arrivarci: ordinamento con ComparisonChain di Google Guava

Collections.sort(pizzas, new Comparator<Pizza>() {  
    @Override  
    public int compare(Pizza p1, Pizza p2) {  
        return ComparisonChain.start().compare(p1.size, p2.size).compare(p1.nrOfToppings, p2.nrOfToppings).compare(p1.name, p2.name).result();  
        // or in case the fields can be null:  
        /* 
        return ComparisonChain.start() 
           .compare(p1.size, p2.size, Ordering.natural().nullsLast()) 
           .compare(p1.nrOfToppings, p2.nrOfToppings, Ordering.natural().nullsLast()) 
           .compare(p1.name, p2.name, Ordering.natural().nullsLast()) 
           .result(); 
        */  
    }  
});  

Questo è molto meglio, ma richiede un codice di targa della caldaia per il caso d'uso più comune: i valori null dovrebbero essere valutati meno per impostazione predefinita. Per i campi null, devi fornire una direttiva aggiuntiva a Guava che cosa fare in quel caso. Questo è un meccanismo flessibile se vuoi fare qualcosa di specifico, ma spesso vuoi il caso predefinito (es. 1, a, b, z, null).

Ordinamento con Apache Commons CompareToBuilder

Collections.sort(pizzas, new Comparator<Pizza>() {  
    @Override  
    public int compare(Pizza p1, Pizza p2) {  
        return new CompareToBuilder().append(p1.size, p2.size).append(p1.nrOfToppings, p2.nrOfToppings).append(p1.name, p2.name).toComparison();  
    }  
});  

Come Guison's ComparisonChain, questa classe di libreria ordina facilmente su più campi, ma definisce anche il comportamento predefinito per i valori null (es. 1, a, b, z, null). Tuttavia, non è possibile specificare nient'altro, a meno che non si fornisca il proprio comparatore.

così

Alla fine si riduce al sapore e alla necessità di flessibilità (Guava's ComparisonChain) rispetto al codice conciso (Apache's CompareToBuilder).

Metodo bonus

Ho trovato una bella soluzione che combina più comparatori in ordine di priorità su CodeReview in un MultiComparator:

class MultiComparator<T> implements Comparator<T> {
    private final List<Comparator<T>> comparators;

    public MultiComparator(List<Comparator<? super T>> comparators) {
        this.comparators = comparators;
    }

    public MultiComparator(Comparator<? super T>... comparators) {
        this(Arrays.asList(comparators));
    }

    public int compare(T o1, T o2) {
        for (Comparator<T> c : comparators) {
            int result = c.compare(o1, o2);
            if (result != 0) {
                return result;
            }
        }
        return 0;
    }

    public static <T> void sort(List<T> list, Comparator<? super T>... comparators) {
        Collections.sort(list, new MultiComparator<T>(comparators));
    }
}

Ofcourse Apache Commons Collections ha già un util per questo:

ComparatorUtils.chainedComparator (comparatorCollection)

Collections.sort(list, ComparatorUtils.chainedComparator(comparators));

22

@Patrick Per ordinare più di un campo consecutivamente prova ComparatorChain

Un ComparatorChain è un comparatore che avvolge uno o più comparatori in sequenza. ComparatorChain chiama ciascun comparatore in sequenza fino a quando 1) ogni singolo comparatore restituisce un risultato diverso da zero (e tale risultato viene quindi restituito), oppure 2) il ComparatorChain viene esaurito (e viene restituito zero). Questo tipo di ordinamento è molto simile all'ordinamento a più colonne in SQL e questa classe consente alle classi Java di emulare quel tipo di comportamento durante l'ordinamento di un elenco.

Per facilitare ulteriormente l'ordinamento simile a SQL, è possibile invertire l'ordine di ogni singolo comparatore nell'elenco.

La chiamata di un metodo che aggiunge nuovi comparatori o modifica l'ordinamento ascendente / discendente dopo che è stato chiamato il confronto (oggetto, oggetto) comporterà un UnsupportedOperationException. Tuttavia, fare attenzione a non modificare l'elenco di comparatori sottostante o il BitSet che definisce l'ordinamento.

Le istanze di ComparatorChain non sono sincronizzate. La classe non è thread-safe in fase di costruzione, ma è thread-safe per eseguire confronti multipli dopo il completamento di tutte le operazioni di installazione.


20

Un'altra opzione che puoi sempre considerare è Apache Commons. Offre molte opzioni.

import org.apache.commons.lang3.builder.CompareToBuilder;

Ex:

public int compare(Person a, Person b){

   return new CompareToBuilder()
     .append(a.getName(), b.getName())
     .append(a.getAddress(), b.getAddress())
     .toComparison();
}


10
import com.google.common.collect.ComparisonChain;

/**
 * @author radler
 * Class Description ...
 */
public class Attribute implements Comparable<Attribute> {

    private String type;
    private String value;

    public String getType() { return type; }
    public void setType(String type) { this.type = type; }

    public String getValue() { return value; }
    public void setValue(String value) { this.value = value; }

    @Override
    public String toString() {
        return "Attribute [type=" + type + ", value=" + value + "]";
    }

    @Override
    public int compareTo(Attribute that) {
        return ComparisonChain.start()
            .compare(this.type, that.type)
            .compare(this.value, that.value)
            .result();
    }

}

1
Mi piace molto questa strategia. Grazie!
Mr. Polywhirl,

Il modo più efficace! Grazie
Zakaria Bouazza il

8

Per coloro che sono in grado di utilizzare l'API di streaming Java 8, esiste un approccio più pulito che è ben documentato qui: Lambdas e ordinamento

Stavo cercando l'equivalente di C # LINQ:

.ThenBy(...)

Ho trovato il meccanismo in Java 8 sul comparatore:

.thenComparing(...)

Quindi, ecco lo snippet che dimostra l'algoritmo.

    Comparator<Person> comparator = Comparator.comparing(person -> person.name);
    comparator = comparator.thenComparing(Comparator.comparing(person -> person.age));

Dai un'occhiata al link sopra per un modo più ordinato e una spiegazione su come l'inferenza del tipo di Java rende un po 'più goffo da definire rispetto a LINQ.

Ecco il test unitario completo per riferimento:

@Test
public void testChainedSorting()
{
    // Create the collection of people:
    ArrayList<Person> people = new ArrayList<>();
    people.add(new Person("Dan", 4));
    people.add(new Person("Andi", 2));
    people.add(new Person("Bob", 42));
    people.add(new Person("Debby", 3));
    people.add(new Person("Bob", 72));
    people.add(new Person("Barry", 20));
    people.add(new Person("Cathy", 40));
    people.add(new Person("Bob", 40));
    people.add(new Person("Barry", 50));

    // Define chained comparators:
    // Great article explaining this and how to make it even neater:
    // http://blog.jooq.org/2014/01/31/java-8-friday-goodies-lambdas-and-sorting/
    Comparator<Person> comparator = Comparator.comparing(person -> person.name);
    comparator = comparator.thenComparing(Comparator.comparing(person -> person.age));

    // Sort the stream:
    Stream<Person> personStream = people.stream().sorted(comparator);

    // Make sure that the output is as expected:
    List<Person> sortedPeople = personStream.collect(Collectors.toList());
    Assert.assertEquals("Andi",  sortedPeople.get(0).name); Assert.assertEquals(2,  sortedPeople.get(0).age);
    Assert.assertEquals("Barry", sortedPeople.get(1).name); Assert.assertEquals(20, sortedPeople.get(1).age);
    Assert.assertEquals("Barry", sortedPeople.get(2).name); Assert.assertEquals(50, sortedPeople.get(2).age);
    Assert.assertEquals("Bob",   sortedPeople.get(3).name); Assert.assertEquals(40, sortedPeople.get(3).age);
    Assert.assertEquals("Bob",   sortedPeople.get(4).name); Assert.assertEquals(42, sortedPeople.get(4).age);
    Assert.assertEquals("Bob",   sortedPeople.get(5).name); Assert.assertEquals(72, sortedPeople.get(5).age);
    Assert.assertEquals("Cathy", sortedPeople.get(6).name); Assert.assertEquals(40, sortedPeople.get(6).age);
    Assert.assertEquals("Dan",   sortedPeople.get(7).name); Assert.assertEquals(4,  sortedPeople.get(7).age);
    Assert.assertEquals("Debby", sortedPeople.get(8).name); Assert.assertEquals(3,  sortedPeople.get(8).age);
    // Andi     : 2
    // Barry    : 20
    // Barry    : 50
    // Bob      : 40
    // Bob      : 42
    // Bob      : 72
    // Cathy    : 40
    // Dan      : 4
    // Debby    : 3
}

/**
 * A person in our system.
 */
public static class Person
{
    /**
     * Creates a new person.
     * @param name The name of the person.
     * @param age The age of the person.
     */
    public Person(String name, int age)
    {
        this.age = age;
        this.name = name;
    }

    /**
     * The name of the person.
     */
    public String name;

    /**
     * The age of the person.
     */
    public int age;

    @Override
    public String toString()
    {
        if (name == null) return super.toString();
        else return String.format("%s : %d", this.name, this.age);
    }
}

7

Scrivere a Comparator manualmente un tale caso d'uso è una terribile soluzione IMO. Tali approcci ad hoc presentano molti inconvenienti:

  • Nessun riutilizzo del codice. Viola SECCO.
  • Boilerplate.
  • Maggiore possibilità di errori.

Quindi qual è la soluzione?

Prima una teoria.

Indichiamo la proposizione "tipo Asupporta il confronto" di Ord A. (Dal punto di vista del programma, puoi pensare Ord Aa un oggetto contenente una logica per confrontare due Asecondi. Sì, proprio comeComparator .)

Ora, se Ord Ae Ord B, allora anche il loro composito (A, B)dovrebbe supportare il confronto. vale a dire Ord (A, B). Se Ord A,, Ord Be Ord C, quindi Ord (A, B, C).

Possiamo estendere questo argomento all'arità arbitraria e dire:

Ord A, Ord B, Ord C, ..., Ord ZOrd (A, B, C, .., Z)

Chiamiamo questa affermazione 1.

Il confronto dei compositi funzionerà esattamente come descritto nella tua domanda: il primo confronto verrà provato prima, poi il successivo, poi il successivo e così via.

Questa è la prima parte della nostra soluzione. Ora la seconda parte.

Se si sa che Ord A, e sapere come trasformare Ba A(chiamata funzione di trasformazione f), allora si può anche avere Ord B. Come? Bene, quando le due Bistanze devono essere confrontate, le trasformi prima in Auso fe poi in applicazione Ord A.

Qui, stiamo mappando la trasformazione B → Aa Ord A → Ord B. Questo è noto come mappatura controvariante (o comapin breve).

Ord A, (B → A)comap Ord B

Chiamiamo questa affermazione 2.


Ora applichiamo questo al tuo esempio.

Hai un tipo di dati denominato Personche comprende tre campi di tipo String.

  • Lo sappiamo Ord String. Dalla dichiarazione 1, Ord (String, String, String).

  • Possiamo facilmente scrivere una funzione da Persona (String, String, String). (Restituisci solo i tre campi.) Dal momento che sappiamo Ord (String, String, String)e Person → (String, String, String), con la dichiarazione 2, possiamo usare comapper ottenere Ord Person.

QED.


Come posso implementare tutti questi concetti?

La buona notizia è che non è necessario. Esiste già una biblioteca che implementa tutte le idee descritte in questo post. (Se sei curioso di sapere come vengono implementate, puoi guardare sotto il cofano .)

Ecco come apparirà il codice:

Ord<Person> personOrd = 
 p3Ord(stringOrd, stringOrd, stringOrd).comap(
   new F<Person, P3<String, String, String>>() {
     public P3<String, String, String> f(Person x) {
       return p(x.getFirstName(), x.getLastname(), x.getAge());
     }
   }
 );

Spiegazione:

  • stringOrdè un oggetto di tipo Ord<String>. Ciò corrisponde alla nostra proposta originale di "supporto comparativo".
  • p3Ordè un metodo che prende Ord<A>, Ord<B>, Ord<C>, e ritorna Ord<P3<A, B, C>>. Ciò corrisponde alla dichiarazione 1. ( P3sta per prodotto con tre elementi. Prodotto è un termine algebrico per i composti.)
  • comapcorrisponde a bene comap,.
  • F<A, B>rappresenta una funzione di trasformazione A → B.
  • p è un metodo di fabbrica per la creazione di prodotti.
  • L'intera espressione corrisponde all'istruzione 2.

Spero che aiuti.


5

Invece di metodi di confronto, potresti voler definire diversi tipi di sottoclassi "Comparator" all'interno della classe Person. In questo modo puoi passarli nei metodi di ordinamento delle Collezioni standard.


3

Penso che sarebbe più confuso se il tuo algoritmo di confronto fosse "intelligente". Vorrei andare con i numerosi metodi di confronto che hai suggerito.

L'unica eccezione per me sarebbe l'uguaglianza. Per i test unitari, mi è stato utile sostituire .Equals (in .net) per determinare se diversi campi sono uguali tra due oggetti (e non che i riferimenti sono uguali).


3

Se ci sono diversi modi in cui un utente può ordinare una persona, si può anche avere più impostazioni del comparatore come costanti da qualche parte. La maggior parte delle operazioni di ordinamento e raccolte ordinate prendono un comparatore come parametro.


3
//Following is the example in jdk 1.8
package com;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

class User {
    private String firstName;
    private String lastName;
    private Integer age;

    public Integer getAge() {
        return age;
    }

    public User setAge(Integer age) {
        this.age = age;
        return this;
    }

    public String getFirstName() {
        return firstName;
    }

    public User setFirstName(String firstName) {
        this.firstName = firstName;
        return this;
    }

    public String getLastName() {
        return lastName;
    }

    public User setLastName(String lastName) {
        this.lastName = lastName;
        return this;
    }

}

public class MultiFieldsComparision {

    public static void main(String[] args) {
        List<User> users = new ArrayList<User>();

        User u1 = new User().setFirstName("Pawan").setLastName("Singh").setAge(38);
        User u2 = new User().setFirstName("Pawan").setLastName("Payal").setAge(37);
        User u3 = new User().setFirstName("Anuj").setLastName("Kumar").setAge(60);
        User u4 = new User().setFirstName("Anuj").setLastName("Kumar").setAge(43);
        User u5 = new User().setFirstName("Pawan").setLastName("Chamoli").setAge(44);
        User u6 = new User().setFirstName("Pawan").setLastName("Singh").setAge(5);

        users.add(u1);
        users.add(u2);
        users.add(u3);
        users.add(u4);
        users.add(u5);
        users.add(u6);

        System.out.println("****** Before Sorting ******");

        users.forEach(user -> {
            System.out.println(user.getFirstName() + " , " + user.getLastName() + " , " + user.getAge());
        });

        System.out.println("****** Aftre Sorting ******");

        users.sort(
                Comparator.comparing(User::getFirstName).thenComparing(User::getLastName).thenComparing(User::getAge));

        users.forEach(user -> {
            System.out.println(user.getFirstName() + " , " + user.getLastName() + " , " + user.getAge());
        });

    }

}

3

L'implementazione del codice dello stesso è qui se dobbiamo ordinare l'oggetto Person in base a più campi.

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class Person {

private String firstName;
private String lastName;
private int age;

public String getFirstName() {
    return firstName;
}

public void setFirstName(String firstName) {
    this.firstName = firstName;
}

public String getLastName() {
    return lastName;
}

public void setLastName(String lastName) {
    this.lastName = lastName;
}

public int getAge() {
    return age;
}

public void setAge(int age) {
    this.age = age;
}

public Person(String firstName, String lastName, int age) {
    this.firstName = firstName;
    this.lastName = lastName;
    this.age = age;
}


static class PersonSortingComparator implements Comparator<Person> {

    @Override
    public int compare(Person person1, Person person2) {

        // for first name comparison
        int firstNameCompare = person1.getFirstName().compareTo(person2.getFirstName());

        // for last name comparison
        int lastNameCompare = person1.getLastName().compareTo(person2.getLastName());

        // for last name comparison
        int ageCompare = person1.getAge() - person2.getAge();

        // Now comparing
        if (firstNameCompare == 0) {
            if (lastNameCompare == 0) {
                return ageCompare;
            }
            return lastNameCompare;
        }
        return firstNameCompare;
    }
}

public static void main(String[] args) {
    Person person1 = new Person("Ajay", "Kumar", 27);
    Person person2 = new Person("Ajay","Gupta", 23);
    Person person3 = new Person("Ajay","Kumar", 22);


    ArrayList<Person> persons = new ArrayList<>();
    persons.add(person1);
    persons.add(person2);
    persons.add(person3);


    System.out.println("Before Sorting:\n");
    for (Person person : persons) {
        System.out.println(person.firstName + " " + person.lastName + " " + person.age);
    }

    Collections.sort(persons, new PersonSortingComparator());

    System.out.println("After Sorting:\n");
    for (Person person : persons) {
        System.out.println(person.firstName + " " + person.lastName + " " + person.age);
    }
}

}

2
//here threshold,buyRange,targetPercentage are three keys on that i have sorted my arraylist 
final Comparator<BasicDBObject> 

    sortOrder = new Comparator<BasicDBObject>() {
                    public int compare(BasicDBObject e1, BasicDBObject e2) {
                        int threshold = new Double(e1.getDouble("threshold"))
                        .compareTo(new Double(e2.getDouble("threshold")));
                        if (threshold != 0)
                            return threshold;

                        int buyRange = new Double(e1.getDouble("buyRange"))
                        .compareTo(new Double(e2.getDouble("buyRange")));
                        if (buyRange != 0)
                            return buyRange;

                        return (new Double(e1.getDouble("targetPercentage")) < new Double(
                                e2.getDouble("targetPercentage")) ? -1 : (new Double(
                                        e1.getDouble("targetPercentage")) == new Double(
                                                e2.getDouble("targetPercentage")) ? 0 : 1));
                    }
                };
                Collections.sort(objectList, sortOrder);

Sono arrivato a questa domanda perché il mio codice ha iniziato ad apprezzare la tua risposta;)
Jan Groth,

0

Se implementi l' interfaccia Comparable , ti consigliamo di scegliere una proprietà semplice da ordinare. Questo è noto come ordinamento naturale. Pensalo come predefinito. Viene sempre utilizzato quando non viene fornito un comparatore specifico. Di solito questo è il nome, ma il tuo caso d'uso potrebbe richiedere qualcosa di diverso. Sei libero di utilizzare un numero qualsiasi di altri comparatori che puoi fornire alle varie raccolte API per ignorare l'ordinamento naturale.

Si noti inoltre che in genere se a.compareTo (b) == 0, quindi a.equals (b) == true. Va bene se no, ma ci sono effetti collaterali di cui essere consapevoli. Guarda l'eccellente javadocs sull'interfaccia Comparable e troverai molte ottime informazioni su questo.


0

Di seguito blog dato un buon esempio di confronto concatenato

http://www.codejava.net/java-core/collections/sorting-a-list-by-multiple-attributes-example

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

/**
 * This is a chained comparator that is used to sort a list by multiple
 * attributes by chaining a sequence of comparators of individual fields
 * together.
 *
 */
public class EmployeeChainedComparator implements Comparator<Employee> {

    private List<Comparator<Employee>> listComparators;

    @SafeVarargs
    public EmployeeChainedComparator(Comparator<Employee>... comparators) {
        this.listComparators = Arrays.asList(comparators);
    }

    @Override
    public int compare(Employee emp1, Employee emp2) {
        for (Comparator<Employee> comparator : listComparators) {
            int result = comparator.compare(emp1, emp2);
            if (result != 0) {
                return result;
            }
        }
        return 0;
    }
}

Comparatore di chiamata:

Collections.sort(listEmployees, new EmployeeChainedComparator(
                new EmployeeJobTitleComparator(),
                new EmployeeAgeComparator(),
                new EmployeeSalaryComparator())
        );

0

A partire dalla risposta di Steve, l'operatore ternario può essere usato:

public int compareTo(Person other) {
    int f = firstName.compareTo(other.firstName);
    int l = lastName.compareTo(other.lastName);
    return f != 0 ? f : l != 0 ? l : Integer.compare(age, other.age);
}

0

È facile confrontare due oggetti con il metodo hashcode in java`

public class Sample{

  String a=null;
  String b=null;

  public Sample(){
      a="s";
      b="a";
  }
  public Sample(String a,String b){
      this.a=a;
      this.b=b;
  }
  public static void main(String args[]){
      Sample f=new Sample("b","12");
      Sample s=new Sample("b","12");
      //will return true
      System.out.println((s.a.hashCode()+s.b.hashCode())==(f.a.hashCode()+f.b.hashCode()));

      //will return false
      Sample f=new Sample("b","12");
      Sample s=new Sample("b","13");
      System.out.println((s.a.hashCode()+s.b.hashCode())==(f.a.hashCode()+f.b.hashCode()));

}

Per favore, non farlo. Gli hashcode non devono essere utilizzati per confronti di uguaglianza ma per l'indicizzazione hashtable. Le collisioni di hash possono provocare l'uguaglianza per due diversi oggetti. Anche gli hashtable si basano sulla reale uguaglianza se si verificano collisioni di hash.
Noel Widmer,

0

Di solito sostituisco il mio compareTo()metodo in questo modo ogni volta che devo fare l'ordinamento multilivello.

public int compareTo(Song o) {
    // TODO Auto-generated method stub
    int comp1 = 10000000*(movie.compareTo(o.movie))+1000*(artist.compareTo(o.artist))+songLength;
    int comp2 = 10000000*(o.movie.compareTo(movie))+1000*(o.artist.compareTo(artist))+o.songLength;
    return comp1-comp2;
} 

Qui la prima preferenza è data al nome del film, quindi all'artista e, infine, a songLength. Devi solo assicurarti che quei moltiplicatori siano abbastanza distanti da non attraversare i confini reciproci.


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.