Sortieren einer ArrayList von Objekten mithilfe einer benutzerdefinierten Sortierreihenfolge


119

Ich möchte eine Sortierfunktion für meine Adressbuchanwendung implementieren.

Ich möchte eine sortieren ArrayList<Contact> contactArray. Contactist eine Klasse, die vier Felder enthält: Name, Privatnummer, Handynummer und Adresse. Ich möchte weiter sortieren name.

Wie kann ich dazu eine benutzerdefinierte Sortierfunktion schreiben?

Antworten:


270

Hier ist ein Tutorial zum Bestellen von Objekten:

Obwohl ich einige Beispiele geben werde, würde ich empfehlen, es trotzdem zu lesen.


Es gibt verschiedene Möglichkeiten, eine zu sortieren ArrayList. Wenn Sie eine natürliche (Standard-) Reihenfolge definieren möchten, müssen Sie die ContactImplementierung zulassen Comparable. Angenommen, Sie möchten standardmäßig nach sortieren name, dann tun Sie dies (der Einfachheit halber wurden keine Nullprüfungen durchgeführt):

public class Contact implements Comparable<Contact> {

    private String name;
    private String phone;
    private Address address;

    public int compareTo(Contact other) {
        return name.compareTo(other.name);
    }

    // Add/generate getters/setters and other boilerplate.
}

damit du es einfach machen kannst

List<Contact> contacts = new ArrayList<Contact>();
// Fill it.

Collections.sort(contacts);

Wenn Sie eine externe steuerbare Reihenfolge definieren möchten (die die natürliche Reihenfolge überschreibt), müssen Sie Folgendes erstellen Comparator:

List<Contact> contacts = new ArrayList<Contact>();
// Fill it.

// Now sort by address instead of name (default).
Collections.sort(contacts, new Comparator<Contact>() {
    public int compare(Contact one, Contact other) {
        return one.getAddress().compareTo(other.getAddress());
    }
}); 

Sie können das Comparators sogar in Contactsich selbst definieren , damit Sie es wiederverwenden können, anstatt es jedes Mal neu zu erstellen:

public class Contact {

    private String name;
    private String phone;
    private Address address;

    // ...

    public static Comparator<Contact> COMPARE_BY_PHONE = new Comparator<Contact>() {
        public int compare(Contact one, Contact other) {
            return one.phone.compareTo(other.phone);
        }
    };

    public static Comparator<Contact> COMPARE_BY_ADDRESS = new Comparator<Contact>() {
        public int compare(Contact one, Contact other) {
            return one.address.compareTo(other.address);
        }
    };

}

die wie folgt verwendet werden kann:

List<Contact> contacts = new ArrayList<Contact>();
// Fill it.

// Sort by address.
Collections.sort(contacts, Contact.COMPARE_BY_ADDRESS);

// Sort later by phone.
Collections.sort(contacts, Contact.COMPARE_BY_PHONE);

Und um das Oberteil abzurunden , könnten Sie einen generischen Javabean-Komparator verwenden :

public class BeanComparator implements Comparator<Object> {

    private String getter;

    public BeanComparator(String field) {
        this.getter = "get" + field.substring(0, 1).toUpperCase() + field.substring(1);
    }

    public int compare(Object o1, Object o2) {
        try {
            if (o1 != null && o2 != null) {
                o1 = o1.getClass().getMethod(getter, new Class[0]).invoke(o1, new Object[0]);
                o2 = o2.getClass().getMethod(getter, new Class[0]).invoke(o2, new Object[0]);
            }
        } catch (Exception e) {
            // If this exception occurs, then it is usually a fault of the developer.
            throw new RuntimeException("Cannot compare " + o1 + " with " + o2 + " on " + getter, e);
        }

        return (o1 == null) ? -1 : ((o2 == null) ? 1 : ((Comparable<Object>) o1).compareTo(o2));
    }

}

die Sie wie folgt verwenden können:

// Sort on "phone" field of the Contact bean.
Collections.sort(contacts, new BeanComparator("phone"));

(Wie Sie im Code sehen, sind möglicherweise bereits Nullfelder abgedeckt, um NPEs beim Sortieren zu vermeiden.)


2
Ich würde die Möglichkeit hinzufügen, mehrere Komparatoren vorab zu definieren und sie dann namentlich zu verwenden ...
Stobor

2
Tatsächlich habe ich es gerade getan. Einfacher als mich selbst zu erklären.
Stobor

@ BalusC: Keine Probleme. Ich kann die Idee nicht würdigen, ich habe sie von String.CASE_INSENSITIVE_ORDERund Freunden bekommen, aber ich mag sie. Erleichtert das Lesen des resultierenden Codes.
Stobor

1
Diese Komparatordefinitionen sollten wahrscheinlich auch sein staticund vielleicht finalauch ... oder so ähnlich ...
Stobor

Heheh ... BeanComparator ist wie Awesome On A Stick! :-) (Ich erinnere mich nicht an den genauen logischen Vergleich von Nullen, aber will es eine (o1 == null && o2 == null) ? 0 :am Anfang dieser Rückleitung?)
Stobor

28

Zusätzlich zu dem, was bereits veröffentlicht wurde, sollten Sie wissen, dass wir seit Java 8 unseren Code verkürzen und wie folgt schreiben können:

Collection.sort(yourList, Comparator.comparing(YourClass::getFieldToSortOn));

oder da List jetzt sortMethode hat

yourList.sort(Comparator.comparing(YourClass::getFieldToSortOn));

Erläuterung:

Seit Java 8 können funktionale Schnittstellen (Schnittstellen mit nur einer abstrakten Methode - sie können mehr Standard- oder statische Methoden haben) einfach implementiert werden mit:

Da Comparator<T>es nur eine abstrakte Methode gibt, handelt int compare(T o1, T o2)es sich um eine funktionale Schnittstelle.

Also statt (Beispiel aus @ BalusC Antwort )

Collections.sort(contacts, new Comparator<Contact>() {
    public int compare(Contact one, Contact other) {
        return one.getAddress().compareTo(other.getAddress());
    }
}); 

Wir können diesen Code reduzieren auf:

Collections.sort(contacts, (Contact one, Contact other) -> {
     return one.getAddress().compareTo(other.getAddress());
});

Wir können dieses (oder jedes) Lambda durch Überspringen vereinfachen

  • Argumenttypen (Java leitet sie basierend auf der Methodensignatur ab)
  • oder {return...}

Also statt

(Contact one, Contact other) -> {
     return one.getAddress().compareTo(other.getAddress();
}

wir können schreiben

(one, other) -> one.getAddress().compareTo(other.getAddress())

Außerdem gibt es jetzt Comparatorstatische Methoden wie comparing(FunctionToComparableValue)oder, mit comparing(FunctionToValue, ValueComparator)denen wir auf einfache Weise Komparatoren erstellen können, die bestimmte Werte von Objekten vergleichen sollen.

Mit anderen Worten, wir können den obigen Code als umschreiben

Collections.sort(contacts, Comparator.comparing(Contact::getAddress)); 
//assuming that Address implements Comparable (provides default order).

8

Auf dieser Seite finden Sie alles, was Sie über das Sortieren von Sammlungen wie ArrayList wissen müssen.

Grundsätzlich müssen Sie

  • Lassen Sie Ihre ContactKlasse die ComparableSchnittstelle von implementieren
    • Erstellen einer Methode public int compareTo(Contact anotherContact)darin.
  • Sobald Sie dies tun, können Sie einfach anrufen Collections.sort(myContactList);,
    • wo myContactListist ArrayList<Contact>(oder eine andere Sammlung von Contact).

Es gibt auch eine andere Möglichkeit, eine Comparator-Klasse zu erstellen, und Sie können dies auch auf der verlinkten Seite nachlesen.

Beispiel:

public class Contact implements Comparable<Contact> {

    ....

    //return -1 for less than, 0 for equals, and 1 for more than
    public compareTo(Contact anotherContact) {
        int result = 0;
        result = getName().compareTo(anotherContact.getName());
        if (result != 0)
        {
            return result;
        }
        result = getNunmber().compareTo(anotherContact.getNumber());
        if (result != 0)
        {
            return result;
        }
        ...
    }
}

5

BalusC und bguiz haben bereits sehr vollständige Antworten zur Verwendung der in Java integrierten Komparatoren gegeben.

Ich möchte nur hinzufügen, dass Google-Sammlungen eine Bestellklasse haben , die "leistungsfähiger" ist als die Standardkomparatoren. Es könnte sich lohnen, einen Blick darauf zu werfen. Sie können coole Dinge tun, z. B. Ordnungen zusammensetzen, umkehren, je nach Ergebnis einer Funktion für Ihre Objekte ordnen ...

Hier ist ein Blog-Beitrag, in dem einige seiner Vorteile erwähnt werden.


Beachten Sie, dass Google-Sammlungen jetzt Teil von Guava (den gängigen Java-Bibliotheken von Google) sind. Sie können sich daher auf Guava (oder das Sammlungsmodul von Guava) verlassen, wenn Sie die Ordering-Klasse verwenden möchten.
Etienne Neveu

4

Sie müssen Ihre Contact-Klassen Comparable implementieren lassen und dann die compareTo(Contact)Methode implementieren . Auf diese Weise kann Collections.sort sie für Sie sortieren. Auf der Seite, auf die ich verlinkt habe, gibt compareTo 'eine negative Ganzzahl, eine Null oder eine positive Ganzzahl zurück, da dieses Objekt kleiner, gleich oder größer als das angegebene Objekt ist.'

Wenn Sie beispielsweise nach Namen (von A bis Z) sortieren möchten, sieht Ihre Klasse folgendermaßen aus:

public class Contact implements Comparable<Contact> {

    private String name;

    // all the other attributes and methods

    public compareTo(Contact other) {
        return this.name.compareTo(other.name);
    }
}

Hat gut mit mir funktioniert, danke! Ich habe auch compareToIgnoreCase verwendet, um Groß- und Kleinschreibung zu ignorieren.
Rani Kheir

3

Mit Lambdaj können Sie eine Sammlung Ihrer Kontakte (z. B. nach ihrem Namen) wie folgt sortieren

sort(contacts, on(Contact.class).getName());

oder durch ihre Adresse:

sort(contacts, on(Contacts.class).getAddress());

und so weiter. Im Allgemeinen bietet es ein DSL, mit dem Sie auf vielfältige Weise auf Ihre Sammlungen zugreifen und diese bearbeiten können, z. B. indem Sie Ihre Kontakte unter bestimmten Bedingungen filtern oder gruppieren, einige ihrer Eigenschaftswerte aggregieren usw.


0

Die Collections.sort ist eine gute Sortierimplementierung. Wenn Sie The Comparable for Contact nicht implementiert haben, müssen Sie eine Comparator-Implementierung übergeben

Bemerkenswert:

Der Sortieralgorithmus ist eine modifizierte Zusammenführungssortierung (bei der die Zusammenführung weggelassen wird, wenn das höchste Element in der unteren Unterliste kleiner als das niedrigste Element in der hohen Unterliste ist). Dieser Algorithmus bietet eine garantierte Leistung von n log (n). Die angegebene Liste muss modifizierbar sein, darf jedoch nicht in der Größe geändert werden. Diese Implementierung speichert die angegebene Liste in einem Array, sortiert das Array und durchläuft die Liste, wobei jedes Element von der entsprechenden Position im Array zurückgesetzt wird. Dadurch wird die Leistung des n2-Protokolls (n) vermieden, die sich aus dem Versuch ergeben würde, eine verknüpfte Liste zu sortieren.

Die Zusammenführungssortierung ist wahrscheinlich besser als die meisten Suchalgorithmen, die Sie ausführen können.


0

Ich habe es folgendermaßen gemacht. Nummer und Name sind zwei Arraylisten. Ich muss den Namen sortieren. Wenn sich die Reihenfolge der Namensarralisten ändert, ändert auch die Reihenfolge der Arraylisten.

public void sortval(){

        String tempname="",tempnum="";

         if (name.size()>1) // check if the number of orders is larger than 1
            {
                for (int x=0; x<name.size(); x++) // bubble sort outer loop
                {
                    for (int i=0; i < name.size()-x-1; i++) {
                        if (name.get(i).compareTo(name.get(i+1)) > 0)
                        {

                            tempname = name.get(i);

                            tempnum=number.get(i);


                           name.set(i,name.get(i+1) );
                           name.set(i+1, tempname);

                            number.set(i,number.get(i+1) );
                            number.set(i+1, tempnum);


                        }
                    }
                }
            }



}

Sie werden länger brauchen, um dies zu schreiben, eine weniger optimale Sortierleistung zu erzielen, mehr Fehler (und hoffentlich mehr Tests) zu schreiben, und es wird schwieriger sein, den Code auf andere Personen zu übertragen. Es ist also nicht richtig. Es mag funktionieren, aber es macht es nicht richtig.
Eric

0

Verwenden Sie diese Methode:

private ArrayList<myClass> sortList(ArrayList<myClass> list) {
    if (list != null && list.size() > 1) {
        Collections.sort(list, new Comparator<myClass>() {
            public int compare(myClass o1, myClass o2) {
                if (o1.getsortnumber() == o2.getsortnumber()) return 0;
                return o1.getsortnumber() < o2.getsortnumber() ? 1 : -1;
            }
        });
    }
    return list;
}

`

und Verwendung: Sie müssen mySortedlist = sortList(myList); keinen Komparator in Ihrer Klasse implementieren. Wenn Sie in umgekehrter Reihenfolge tauschen möchten 1und-1


0

Ok, ich weiß, dass dies vor langer Zeit beantwortet wurde ... aber hier sind einige neue Informationen:

Angenommen, die betreffende Kontaktklasse hat bereits eine definierte natürliche Reihenfolge, indem Comparable implementiert wird. Sie möchten diese Reihenfolge jedoch überschreiben, beispielsweise nach Namen. Hier ist die moderne Vorgehensweise:

List<Contact> contacts = ...;

contacts.sort(Comparator.comparing(Contact::getName).reversed().thenComparing(Comparator.naturalOrder());

Auf diese Weise wird zuerst nach Namen sortiert (in umgekehrter Reihenfolge), und dann wird bei Namenskollisionen auf die von der Contact-Klasse selbst implementierte 'natürliche' Reihenfolge zurückgegriffen.


-1

Sie sollten die Funktion Arrays.sort verwenden. Die enthaltenen Klassen sollten Comparable implementieren.


Deshalb habe ich Arrays gesagt.
Mönch

Das Problem ist, dass OP ArrayList verwendet, nicht Array.
Pshemo
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.