So vergleichen Sie Objekte anhand mehrerer Felder

236

Angenommen, Sie haben einige Objekte mit mehreren Feldern, mit denen sie verglichen werden können:

public class Person {

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

    /* Constructors */

    /* Methods */

}

Wenn Sie in diesem Beispiel fragen, ob:

a.compareTo(b) > 0

Sie fragen sich vielleicht, ob der Nachname von a vor dem von b steht oder ob a älter als b ist usw.

Was ist der sauberste Weg, um mehrere Vergleiche zwischen diesen Arten von Objekten zu ermöglichen, ohne unnötige Unordnung oder Overhead hinzuzufügen?

  • java.lang.Comparable Schnittstelle ermöglicht den Vergleich nur durch ein Feld
  • Hinzufügen von zahlreichen Methoden zu vergleichen (dh compareByFirstName(), compareByAge()etc ...) ist meiner Meinung nach überladen.

Was ist der beste Weg, um dies zu erreichen?

Yuval Adam
quelle
3
Warum ist das ein CW? Es ist eine vollkommen gültige Programmierfrage.
Elie
2
Ist Ihnen bewusst, dass Comparable den Vergleich mit so vielen Feldern ermöglicht, wie Sie möchten?
DJClayworth

Antworten:

81

Sie können ein implementieren, Comparatordas zwei PersonObjekte vergleicht , und Sie können so viele Felder untersuchen, wie Sie möchten. Sie können eine Variable in Ihren Komparator einfügen, die angibt, mit welchem ​​Feld verglichen werden soll, obwohl es wahrscheinlich einfacher wäre, nur mehrere Komparatoren zu schreiben.

Elie
quelle
5
Eigentlich bevorzuge ich die Idee, einen einzigen Komparator zu verwenden. Ich denke nicht, dass diese Antwort falsch ist, aber jeder, der sie liest, sollte auf jeden Fall die Antwort von Steve Kuo unten überprüfen.
Felipe Leão
Die Mehrfachkomparatoren waren nur verfügbar, wenn Sie unterschiedliche Vergleichsmethoden wünschen, die nicht von den Daten selbst abhängen - dh manchmal möchten Sie nach Namen, manchmal nach Alter usw. vergleichen. Zum Vergleichen nach mehreren Feldern gleichzeitig nur ein Komparator wäre notwendig.
Elie
399

Mit Java 8:

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

Wenn Sie Zugriffsmethoden haben:

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

Wenn eine Klasse Comparable implementiert, kann ein solcher Komparator in der compareTo-Methode verwendet werden:

@Override
public int compareTo(Person o){
    return Comparator.comparing(Person::getFirstName)
              .thenComparing(Person::getLastName)
              .thenComparingInt(Person::getAge)
              .compare(this, o);
}
Enigo
quelle
5
Besonders die Besetzung (Person p)ist wichtig für verkettete Komparatoren.
Membersound
5
Wie effizient ist es, eine große Anzahl von Objekten zu vergleichen, z. B. beim Sortieren? Muss es Comparatorbei jedem Aufruf neue Instanzen erstellen ?
JJurm
4
Ich erhalte eine NullPointerException, wenn eines der Felder, die ich vergleiche, null ist, wie ein String. Gibt es eine Möglichkeit, dieses Vergleichsformat beizubehalten, aber zuzulassen, dass es nullsicher ist?
Erreichen Sie
3
@jjurm .thenComparing(Person::getLastName, Comparator.nullsFirst(Comparator.naturalOrder()))- zuerst Feldauswahl , dann Komparator
Gavenkoa
2
@jjurm, wenn Sie es compareTowie oben gezeigt verwenden, Comparatorwird bei jedem Aufruf der Methode erstellt. Sie können dies verhindern, indem Sie den Komparator in einem privaten statischen Endfeld speichern.
Gandalf
165

Sie sollten implementieren Comparable <Person>. Unter der Annahme, dass nicht alle Felder null sind (der Einfachheit halber), dass das Alter ein int ist und das Vergleichsranking das erste, letzte, Alter ist, ist die compareToMethode recht einfach:

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);
}
Steve Kuo
quelle
10
Wenn Sie Comparable <Person> implementieren, lautet die Methode compareTo (Person p). Es scheint, dass diese Antwort mit der Vergleichsmethode <T o1, T o2> von Comparator verwechselt wurde
Mike
5
Dies wird nicht empfohlen. Verwenden Sie Comparator, wenn Sie mehrere Felder haben.
Indika
1
Das ist im Moment die beste Lösung (besser als mehr Komparatoren)
Vasile Surdu
4
@indika, ich bin neugierig: warum wird das nicht empfohlen? Der Vergleich mit mehr als einer Eigenschaft scheint mir vollkommen in Ordnung zu sein.
Ars-Longa-Vita-Brevis
4
@ ars-longa-vita-brevis, Wenn Sie Comparable verwenden, muss sich die Sortierlogik in derselben Klasse befinden, deren Objekte sortiert werden. Dies wird als natürliche Reihenfolge von Objekten bezeichnet. Mit der Verwendung von Comparator können Sie eine benutzerdefinierte Sortierlogik außerhalb der Person-Klasse schreiben. Wenn Sie Personenobjekte nur anhand ihres Vor- oder Nachnamens vergleichen möchten, können Sie diese Logik nicht verwenden. Sie müssen es noch einmal schreiben,
Indika
78

(von Möglichkeiten zum Sortieren von Objektlisten in Java anhand mehrerer Felder )

Arbeitscode in diesem Kern

Verwenden von Java 8 Lambda (hinzugefügt am 10. April 2019)

Java 8 löst dies gut durch Lambda (obwohl Guava und Apache Commons möglicherweise noch mehr Flexibilität bieten):

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

Vielen Dank an @ gaoagongs Antwort unten .

Chaotisch und verschlungen: Sortieren von Hand

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);  
    }  
});  

Dies erfordert viel Eingabe, Wartung und ist fehleranfällig.

Der reflektierende Weg: Sortieren mit BeanComparator

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

Collections.sort(pizzas, chain);  

Dies ist natürlich prägnanter, aber noch fehleranfälliger, da Sie Ihren direkten Verweis auf die Felder verlieren, indem Sie stattdessen Strings verwenden (keine Typensicherheit, automatische Refactorings). Wenn nun ein Feld umbenannt wird, meldet der Compiler nicht einmal ein Problem. Da diese Lösung Reflexion verwendet, ist die Sortierung außerdem viel langsamer.

Anreise: Sortieren mit Google Guavas ComparisonChain

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(); 
        */  
    }  
});  

Dies ist viel besser, erfordert jedoch einen Kesselplattencode für den häufigsten Anwendungsfall: Nullwerte sollten standardmäßig weniger bewertet werden. Für Nullfelder müssen Sie Guava eine zusätzliche Anweisung geben, was in diesem Fall zu tun ist. Dies ist ein flexibler Mechanismus, wenn Sie etwas Bestimmtes tun möchten, aber häufig den Standardfall (dh 1, a, b, z, null).

Sortieren mit 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();  
    }  
});  

Wie Guavas ComparisonChain sortiert diese Bibliotheksklasse problemlos nach mehreren Feldern, definiert jedoch auch das Standardverhalten für Nullwerte (z. B. 1, a, b, z, null). Sie können jedoch auch nichts anderes angeben, es sei denn, Sie stellen Ihren eigenen Komparator bereit.

So

Letztendlich kommt es auf den Geschmack und das Bedürfnis nach Flexibilität (Guavas ComparisonChain) im Vergleich zu prägnantem Code (Apaches CompareToBuilder) an.

Bonusmethode

Ich fand eine schöne Lösung , dass mehrere Komparatoren in der Reihenfolge ihrer Priorität Mähdrescher auf Codereview in einem 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));
    }
}

Natürlich hat Apache Commons Collections bereits einen Nutzen dafür:

ComparatorUtils.chainedComparator (compareatorCollection)

Collections.sort(list, ComparatorUtils.chainedComparator(comparators));
Benny Bottema
quelle
22

@Patrick Um mehr als ein Feld nacheinander zu sortieren, versuchen Sie es mit ComparatorChain

Eine ComparatorChain ist ein Comparator, der einen oder mehrere Comparators nacheinander umschließt. Die ComparatorChain ruft jeden Comparator nacheinander auf, bis entweder 1) ein einzelner Comparator ein Ergebnis ungleich Null zurückgibt (und dieses Ergebnis dann zurückgegeben wird) oder 2) die ComparatorChain erschöpft ist (und Null zurückgegeben wird). Diese Art der Sortierung ist der mehrspaltigen Sortierung in SQL sehr ähnlich. Mit dieser Klasse können Java-Klassen diese Art von Verhalten beim Sortieren einer Liste emulieren.

Um die SQL-ähnliche Sortierung weiter zu vereinfachen, kann die Reihenfolge jedes einzelnen Komparators in der Liste umgekehrt werden.

Das Aufrufen einer Methode, die nach dem Aufruf von compare (Object, Object) neue Comparators hinzufügt oder die Auf- / Abstiegssortierung ändert, führt zu einer UnsupportedOperationException. Achten Sie jedoch darauf, die zugrunde liegende Liste der Komparatoren oder das BitSet, das die Sortierreihenfolge definiert, nicht zu ändern.

Instanzen von ComparatorChain werden nicht synchronisiert. Die Klasse ist zur Erstellungszeit nicht threadsicher, es ist jedoch threadsicher, mehrere Vergleiche durchzuführen, nachdem alle Setup-Vorgänge abgeschlossen sind.

Nigel_V_Thomas
quelle
20

Eine weitere Option, die Sie immer in Betracht ziehen können, ist Apache Commons. Es bietet viele Optionen.

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();
}
Xeroiris
quelle
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();
    }

}
Ran Adler
quelle
1
Ich mag diese Strategie sehr. Vielen Dank!
Herr Polywhirl
Der effektivste Weg! Danke
Zakaria Bouazza
8

Für diejenigen, die die Java 8-Streaming-API verwenden können, gibt es einen übersichtlichen Ansatz, der hier gut dokumentiert ist: Lambdas und Sortierung

Ich habe nach dem Äquivalent zum C # LINQ gesucht:

.ThenBy(...)

Ich habe den Mechanismus in Java 8 im Komparator gefunden:

.thenComparing(...)

Hier ist also das Snippet, das den Algorithmus demonstriert.

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

Schauen Sie sich den Link oben an, um eine übersichtlichere Art und Weise zu erfahren und zu erklären, wie die Java-Typinferenz die Definition im Vergleich zu LINQ etwas umständlicher macht.

Hier ist der vollständige Unit-Test als Referenz:

@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);
    }
}
Luke Machowski
quelle
7

Das Comparatormanuelle Schreiben eines solchen Anwendungsfalls ist eine schreckliche Lösung, IMO. Solche Ad-hoc-Ansätze haben viele Nachteile:

  • Keine Wiederverwendung von Code. Verstößt gegen DRY.
  • Boilerplate.
  • Erhöhte Fehlerwahrscheinlichkeit.

Was ist die Lösung?

Zuerst eine Theorie.

Bezeichnen wir den Satz "Typ Aunterstützt Vergleich" mit Ord A. (Aus Programmsicht können Sie sich Ord Aein Objekt vorstellen, das Logik zum Vergleichen von zwei As enthält. Ja, genau wie Comparator.)

Wenn Ord Aund Ord B, dann sollte ihr Verbund (A, B)auch den Vergleich unterstützen. dh Ord (A, B). Wenn Ord A, Ord Bund Ord Cdann Ord (A, B, C).

Wir können dieses Argument auf eine willkürliche Arität ausweiten und sagen:

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

Nennen wir diese Aussage 1.

Der Vergleich der Verbundwerkstoffe funktioniert genau so, wie Sie es in Ihrer Frage beschrieben haben: Der erste Vergleich wird zuerst versucht, dann der nächste, dann der nächste und so weiter.

Das ist der erste Teil unserer Lösung. Nun zum zweiten Teil.

Wenn Sie das wissen Ord A, und wissen , wie man transformieren Bzu A(Anruf , dass Transformationsfunktion f), dann können Sie auch haben Ord B. Wie? Nun, wenn die beiden BInstanzen verglichen werden sollen, transformieren Sie sie zuerst in Ausing fund wenden sie dann an Ord A.

Hier ordnen wir die Transformation B → Azu Ord A → Ord B. Dies wird als kontravariante Zuordnung (oder comapkurz) bezeichnet.

Ord A, (B → A)comap Ord B

Nennen wir diese Aussage 2.


Wenden wir dies nun auf Ihr Beispiel an.

Sie haben einen Datentyp mit dem Namen Person, der drei Typfelder umfasst String.

  • Das wissen wir Ord String. Mit Aussage 1 , Ord (String, String, String).

  • Wir können leicht eine Funktion von Personbis schreiben (String, String, String). (Geben Sie einfach die drei Felder zurück.) Da wir wissen Ord (String, String, String)und Person → (String, String, String)nach Aussage 2 können wir verwenden comap, um zu erhalten Ord Person.

QED.


Wie implementiere ich all diese Konzepte?

Die gute Nachricht ist, dass Sie nicht müssen. Es gibt bereits eine Bibliothek, die alle in diesem Beitrag beschriebenen Ideen umsetzt. (Wenn Sie neugierig sind, wie diese implementiert werden, können Sie unter die Haube schauen .)

So sieht der Code damit aus:

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());
     }
   }
 );

Erläuterung:

  • stringOrdist ein Objekt vom Typ Ord<String>. Dies entspricht unserem ursprünglichen Vorschlag "unterstützt den Vergleich".
  • p3Ordist ein Verfahren , das erfordert Ord<A>, Ord<B>, Ord<C>und kehrt zurück Ord<P3<A, B, C>>. Dies entspricht Aussage 1. ( P3steht für Produkt mit drei Elementen. Produkt ist ein algebraischer Begriff für Verbundwerkstoffe.)
  • comapentspricht gut , comap.
  • F<A, B>repräsentiert eine Transformationsfunktion A → B.
  • p ist eine Factory-Methode zum Erstellen von Produkten.
  • Der gesamte Ausdruck entspricht Aussage 2.

Hoffentlich hilft das.

fehlender Faktor
quelle
5

Anstelle von Vergleichsmethoden möchten Sie möglicherweise nur mehrere Arten von "Komparator" -Unterklassen innerhalb der Person-Klasse definieren. Auf diese Weise können Sie sie an Standard-Sortiermethoden für Sammlungen übergeben.

Marc Novakowski
quelle
3

Ich denke, es wäre verwirrender, wenn Ihr Vergleichsalgorithmus "clever" wäre. Ich würde mich für die zahlreichen von Ihnen vorgeschlagenen Vergleichsmethoden entscheiden.

Die einzige Ausnahme für mich wäre Gleichheit. Für Unit-Tests war es für mich hilfreich, die .Equals (in .net) zu überschreiben, um festzustellen, ob mehrere Felder zwischen zwei Objekten gleich sind (und nicht, ob die Referenzen gleich sind).

Michael Haren
quelle
3

Wenn es mehrere Möglichkeiten gibt, wie ein Benutzer eine Person bestellen kann, können Sie auch mehrere Komparatoren als Konstanten irgendwo einrichten . Die meisten Sortiervorgänge und sortierten Sammlungen verwenden einen Komparator als Parameter.

sblundy
quelle
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());
        });

    }

}
Pawan
quelle
3

Die Code-Implementierung desselben ist hier, wenn wir das Personenobjekt nach mehreren Feldern sortieren müssen.

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);
    }
}

}
Sachchit Bansal
quelle
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);
Pradeep Singh
quelle
Ich bin zu dieser Frage gekommen, weil mein Code Ihre Antwort zu mögen begann;)
Jan Groth
0

Wenn Sie die Schnittstelle " Vergleichbar" implementieren, möchten Sie eine einfache Eigenschaft auswählen, nach der sortiert werden soll. Dies ist als natürliche Ordnung bekannt. Betrachten Sie es als Standard. Es wird immer verwendet, wenn kein spezifischer Komparator geliefert wird. Normalerweise ist dies ein Name, aber Ihr Anwendungsfall erfordert möglicherweise etwas anderes. Es steht Ihnen frei, eine beliebige Anzahl anderer Komparatoren zu verwenden, die Sie verschiedenen Sammlungs-APIs zur Verfügung stellen können, um die natürliche Reihenfolge zu überschreiben.

Beachten Sie auch, dass normalerweise a.equals (b) == true ist, wenn a.compareTo (b) == 0 ist. Es ist in Ordnung, wenn nicht, aber es gibt Nebenwirkungen, die Sie beachten müssen. Sehen Sie sich die hervorragenden Javadocs auf der vergleichbaren Oberfläche an und Sie werden viele großartige Informationen dazu finden.

Mark Renouf
quelle
0

Der folgende Blog gibt ein gut verkettetes Vergleichsbeispiel

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;
    }
}

Komparator anrufen:

Collections.sort(listEmployees, new EmployeeChainedComparator(
                new EmployeeJobTitleComparator(),
                new EmployeeAgeComparator(),
                new EmployeeSalaryComparator())
        );
Vaquar Khan
quelle
0

Ausgehend von Steves Antwort kann der ternäre Operator verwendet werden:

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);
}
Gerold Broser
quelle
0

Es ist einfach, zwei Objekte mit der Hashcode-Methode in Java zu vergleichen

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()));

}
Exodus
quelle
Bitte tu das nicht. Hashcodes sollten nicht für Gleichheitsvergleiche verwendet werden, sondern für die Indizierung von Hashtabellen. Hash-Kollisionen können zur Gleichheit von zwei verschiedenen Objekten führen. Selbst Hashtabellen sind auf echte Gleichheit angewiesen, wenn Hash-Kollisionen auftreten.
Noel Widmer
0

Normalerweise überschreibe ich meine compareTo()Methode so, wenn ich mehrstufig sortieren muss.

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;
} 

Hier wird zuerst der Name des Films, dann der Künstler und zuletzt songLength bevorzugt. Sie müssen nur sicherstellen, dass diese Multiplikatoren weit genug entfernt sind, um die Grenzen des anderen nicht zu überschreiten.

Jayanth
quelle