Ordinamento di un ArrayList di oggetti utilizzando un ordinamento personalizzato


119

Sto cercando di implementare una funzione di ordinamento per la mia applicazione della rubrica.

Voglio ordinare un file ArrayList<Contact> contactArray. Contactè una classe che contiene quattro campi: nome, numero di casa, numero di cellulare e indirizzo. Voglio sistemare name.

Come posso scrivere una funzione di ordinamento personalizzata per farlo?

Risposte:


270

Ecco un tutorial sull'ordinazione di oggetti:

Anche se fornirò alcuni esempi, consiglierei di leggerlo comunque.


Esistono vari modi per ordinare un file ArrayList. Se vuoi definire un ordinamento naturale (predefinito) , devi lasciare implementare . Supponendo che si desideri ordinare per impostazione predefinita , quindi fare (nullcheck omessi per semplicità):ContactComparablename

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

in modo che tu possa semplicemente fare

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

Collections.sort(contacts);

Se desideri definire un ordinamento controllabile esterno (che sostituisce l'ordine naturale), devi creare un 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());
    }
}); 

Puoi persino definire i messaggi di posta Comparatorelettronica in Contactsé in modo da poterli riutilizzare invece di ricrearli ogni volta:

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

}

che può essere utilizzato come segue:

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

E per ottenere il massimo, potresti considerare di utilizzare un generico comparatore javabeano :

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

}

che puoi usare come segue:

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

(come puoi vedere nel codice, i campi eventualmente nulli sono già coperti per evitare NPE durante l'ordinamento)


2
Aggiungerei la possibilità di pre-definire diversi comparatori e quindi di usarli per nome ...
Stobor

2
In effetti, l'ho appena fatto. Più facile che cercare di spiegarmi.
Stobor

@BalusC: Nessun problema. Non posso prendermi il merito dell'idea, l'ho presa da String.CASE_INSENSITIVE_ORDERamici e ma mi piace. Rende il codice risultante più facile da leggere.
Stobor

1
Quelle definizioni di comparatore dovrebbero probabilmente essere anche statice forse finalanche ... O qualcosa del genere ..
Stobor

Heheh ... BeanComparator è come Awesome On A Stick! :-) (Non ricordo il confronto logico esatto dei valori nulli, ma ne vuole uno (o1 == null && o2 == null) ? 0 :all'inizio di quella riga di ritorno?)
Stobor

28

Oltre a quanto già pubblicato dovresti sapere che da Java 8 possiamo abbreviare il nostro codice e scriverlo come:

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

o poiché List ora ha sortmetodo

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

Spiegazione:

A partire da Java 8, le interfacce funzionali (interfacce con un solo metodo astratto - possono avere più metodi predefiniti o statici) possono essere facilmente implementate utilizzando:

Poiché Comparator<T>ha un solo metodo astratto int compare(T o1, T o2), è un'interfaccia funzionale.

Quindi invece di (esempio dalla risposta di @BalusC )

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

possiamo ridurre questo codice a:

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

Possiamo semplificare questo (o qualsiasi) lambda saltando

  • tipi di argomenti (Java li dedurrà in base alla firma del metodo)
  • oppure {return...}

Quindi invece di

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

possiamo scrivere

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

Inoltre ora Comparatorha metodi statici come comparing(FunctionToComparableValue)ocomparing(FunctionToValue, ValueComparator) che potremmo usare per creare facilmente comparatori che dovrebbero confrontare alcuni valori specifici dagli oggetti.

In altre parole possiamo riscrivere il codice sopra come

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

8

Questa pagina ti dice tutto quello che devi sapere sull'ordinamento delle raccolte, come ArrayList.

Fondamentalmente devi

  • fai in modo che la tua Contactclasse implementi l' Comparableinterfaccia tramite
    • creando un metodo al public int compareTo(Contact anotherContact)suo interno.
  • Una volta fatto questo, puoi semplicemente chiamare Collections.sort(myContactList);,
    • dove myContactListè ArrayList<Contact>(o qualsiasi altra raccolta di Contact).

C'è anche un altro modo, che coinvolge la creazione di una classe Comparator, e puoi leggere anche su questo dalla pagina collegata.

Esempio:

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 e bguiz hanno già fornito risposte molto complete su come utilizzare i comparatori integrati di Java.

Voglio solo aggiungere che google-collections ha una classe Ordering che è più "potente" dei comparatori standard. Potrebbe valere la pena dare un'occhiata. Puoi fare cose interessanti come ordinare composti, invertirli, ordinare in base al risultato di una funzione per i tuoi oggetti ...

Ecco un post sul blog che menziona alcuni dei suoi vantaggi.


Nota che google-collections ora fa parte di Guava (le librerie java comuni di Google), quindi potresti voler dipendere da Guava (o dal modulo di raccolta di Guava) se desideri utilizzare la classe Ordering.
Etienne Neveu

4

È necessario che le classi Contact implementino Comparable e quindi implementino il compareTo(Contact)metodo. In questo modo, Collections.sort sarà in grado di ordinarli per te. Per la pagina a cui mi sono collegato, compareTo "restituisce un numero intero negativo, zero o un numero intero positivo poiché questo oggetto è minore, uguale o maggiore dell'oggetto specificato."

Ad esempio, se desideri ordinare per nome (dalla A alla Z), la tua classe avrà questo aspetto:

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

Ha funzionato bene con me, grazie! Ho anche usato compareToIgnoreCase per ignorare le maiuscole.
Rani Kheir

3

Utilizzando lambdaj puoi ordinare una raccolta dei tuoi contatti (ad esempio in base al loro nome) come segue

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

o dal loro indirizzo:

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

e così via. Più in generale, offre un DSL per accedere e manipolare le tue raccolte in molti modi, come filtrare o raggruppare i tuoi contatti in base ad alcune condizioni, aggregare alcuni dei loro valori di proprietà, ecc.


0

Il Collections.sort è una buona implementazione di ordinamento. Se non disponi di The comparable implementato per Contact, dovrai passare un'implementazione di Comparator

Di nota:

L'algoritmo di ordinamento è un mergesort modificato (in cui l'unione viene omessa se l'elemento più alto nella sottolista inferiore è minore dell'elemento più basso nella sottolista alta). Questo algoritmo offre prestazioni n log (n) garantite. L'elenco specificato deve essere modificabile, ma non è necessario ridimensionarlo. Questa implementazione scarica l'elenco specificato in un array, ordina l'array e itera sull'elenco ripristinando ogni elemento dalla posizione corrispondente nell'array. Ciò evita le prestazioni n2 log (n) che deriverebbero dal tentativo di ordinare un elenco collegato in posizione.

L'ordinamento di unione è probabilmente migliore della maggior parte degli algoritmi di ricerca che puoi fare.


0

L'ho fatto nel modo seguente. numero e nome sono due arraylist. Devo ordinare il nome. Se si verificano modifiche all'ordine arralista dei nomi, anche l'elenco degli elenchi dei numeri cambia l'ordine.

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


                        }
                    }
                }
            }



}

Ci vorrà più tempo per scrivere questo, ottenere prestazioni di ordinamento meno ottimali, scrivere più bug (e si spera più test) e il codice sarà più difficile da trasferire ad altre persone. Quindi non è giusto. Può funzionare, ma ciò non lo rende corretto.
Eric

0

usa questo metodo:

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

`

e usa: mySortedlist = sortList(myList); non è necessario implementare il comparatore nella tua classe. Se vuoi lo scambio di ordine inverso 1e-1


0

Ok, so che è stata data una risposta molto tempo fa ... ma ecco alcune nuove informazioni:

Supponiamo che la classe Contact in questione abbia già un ordinamento naturale definito tramite l'implementazione di Comparable, ma desideri sovrascrivere tale ordinamento, diciamo per nome. Ecco il modo moderno per farlo:

List<Contact> contacts = ...;

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

In questo modo ordinerà prima per nome (in ordine inverso), quindi per le collisioni di nomi tornerà all'ordinamento "naturale" implementato dalla classe Contact stessa.


-1

Dovresti usare la funzione Arrays.sort. Le classi contenitore dovrebbero implementare Comparable.


Ecco perché ho detto Arrays.
monaco

Il problema è che OP utilizza ArrayList, non array.
Pshemo
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.