Come faccio a ordinare una lista in base a diversi parametri con tempi diversi

Ho una class di nome Person con proprietà multiple, ad esempio:

 public class Person { private int id; private String name, address; // Many more properties. } 

Un sacco di oggetti- Person sono memorizzati in un ArrayList . Voglio ordinare questa lista in base a più parametri di ordinamento e di volta in volta diversa. Ad esempio, potrei volere una volta ordinare per name ascendente e poi decrescente, e un’altra volta semplicemente decrescente.

E non voglio creare i miei metodi di ordinamento (cioè, voglio usare Collections.sort(personList, someComparator) . Qual è la soluzione più elegante che raggiunge questo?

Penso che il tuo approccio enum sia fondamentalmente sano, ma le istruzioni switch necessitano davvero di un approccio più orientato agli oggetti. Prendere in considerazione:

 enum PersonComparator implements Comparator { ID_SORT { public int compare(Person o1, Person o2) { return Integer.valueOf(o1.getId()).compareTo(o2.getId()); }}, NAME_SORT { public int compare(Person o1, Person o2) { return o1.getFullName().compareTo(o2.getFullName()); }}; public static Comparator decending(final Comparator other) { return new Comparator() { public int compare(Person o1, Person o2) { return -1 * other.compare(o1, o2); } }; } public static Comparator getComparator(final PersonComparator... multipleOptions) { return new Comparator() { public int compare(Person o1, Person o2) { for (PersonComparator option : multipleOptions) { int result = option.compare(o1, o2); if (result != 0) { return result; } } return 0; } }; } } 

Un esempio di utilizzo (con un’importazione statica).

 public static void main(String[] args) { List list = null; Collections.sort(list, decending(getComparator(NAME_SORT, ID_SORT))); } 

Puoi creare comparatori per ciascuna delle proprietà che potresti voler ordinare e quindi provare “concatenamento comparatore” 🙂 come questo:

 public class ChainedComparator implements Comparator { private List> simpleComparators; public ChainedComparator(Comparator... simpleComparators) { this.simpleComparators = Arrays.asList(simpleComparators); } public int compare(T o1, T o2) { for (Comparator comparator : simpleComparators) { int result = comparator.compare(o1, o2); if (result != 0) { return result; } } return 0; } } 

Un modo è quello di creare un Comparator che accetta come argomenti un elenco di proprietà da ordinare, come mostra questo esempio.

 public class Person { private int id; private String name, address; public static Comparator getComparator(SortParameter... sortParameters) { return new PersonComparator(sortParameters); } public enum SortParameter { ID_ASCENDING, ID_DESCENDING, NAME_ASCENDING, NAME_DESCENDING, ADDRESS_ASCENDING, ADDRESS_DESCENDING } private static class PersonComparator implements Comparator { private SortParameter[] parameters; private PersonComparator(SortParameter[] parameters) { this.parameters = parameters; } public int compare(Person o1, Person o2) { int comparison; for (SortParameter parameter : parameters) { switch (parameter) { case ID_ASCENDING: comparison = o1.id - o2.id; if (comparison != 0) return comparison; break; case ID_DESCENDING: comparison = o2.id - o1.id; if (comparison != 0) return comparison; break; case NAME_ASCENDING: comparison = o1.name.compareTo(o2.name); if (comparison != 0) return comparison; break; case NAME_DESCENDING: comparison = o2.name.compareTo(o1.name); if (comparison != 0) return comparison; break; case ADDRESS_ASCENDING: comparison = o1.address.compareTo(o2.address); if (comparison != 0) return comparison; break; case ADDRESS_DESCENDING: comparison = o2.address.compareTo(o1.address); if (comparison != 0) return comparison; break; } } return 0; } } } 

Può quindi essere usato nel codice ad esempio in questo modo:

 cp = Person.getComparator(Person.SortParameter.ADDRESS_ASCENDING, Person.SortParameter.NAME_DESCENDING); Collections.sort(personList, cp); 

Un approccio sarebbe quello di comporre Comparator s. Questo potrebbe essere un metodo di libreria (sono sicuro che esiste da qualche parte là fuori).

 public static  Comparator compose( final Comparator primary, final Comparator secondary ) { return new Comparator() { public int compare(T a, T b) { int result = primary.compare(a, b); return result==0 ? secondary.compare(a, b) : result; } [...] }; } 

Uso:

 Collections.sort(people, compose(nameComparator, addressComparator)); 

In alternativa, nota che Collections.sort è un ordinamento stabile. Se le prestazioni non sono assolutamente cruciali, si ordina che sia l’ordine secondario prima del primario.

 Collections.sort(people, addressComparator); Collections.sort(people, nameComparator); 

I comparatori ti consentono di farlo in modo semplice e naturale. È ansible creare singole istanze di comparatori, sia nella class Person stessa, sia in una class di servizio associata alle proprie necessità.
Esempi, utilizzando classi interne anonime:

  public static final Comparator NAME_ASC_ADRESS_DESC = new Comparator() { public int compare(Person p1, Person p2) { int nameOrder = p1.getName().compareTo(p2.getName); if(nameOrder != 0) { return nameOrder; } return -1 * p1.getAdress().comparedTo(p2.getAdress()); // I use explicit -1 to be clear that the order is reversed } }; public static final Comparator ID_DESC = new Comparator() { public int compare(Person p1, Person p2) { return -1 * p1.getId().comparedTo(p2.getId()); // I use explicit -1 to be clear that the order is reversed } }; // and other comparator instances as needed... 

Se ne hai molti, puoi anche strutturare il codice del tuo comparatore in qualsiasi modo desideri. Ad esempio, potresti:

  • ereditare da un altro comparatore,
  • avere un CompositeComparator che aggrega alcuni comparatori esistenti
  • avere un NullComparator che gestisca casi nulli, quindi delegati a un altro comparatore
  • eccetera…

Penso che accoppiare i selezionatori alla class Person, come nella tua risposta, non sia una buona idea, perché collega il confronto (di solito basato sul business) e l’object del modello per avvicinarsi l’un l’altro. Ogni volta che vuoi cambiare / aggiungere qualcosa al selezionatore, devi toccare la class della persona, che di solito è qualcosa che non vuoi fare.

Utilizzando un servizio o qualcosa di simile, che fornisce istanze di Comparator, come proposto da KLE, sembra molto più flessibile ed estensibile.

Il mio approccio è costruito su quello di Yishai. Il divario principale è che non c’è modo di ordinare prima ascendente per un attributo e poi decrescente per un altro. Questo non può essere fatto con le enumerazioni. Per quello ho usato le classi. Perché il SortOrder dipende fortemente dal tipo che ho preferito implementarlo come class interiore della persona.

La class “Persona” con class interna “SortOrder”:

 import java.util.Comparator; public class Person { private int id; private String firstName; private String secondName; public Person(int id, String firstName, String secondName) { this.id = id; this.firstName = firstName; this.secondName = secondName; } public abstract static class SortOrder implements Comparator { public static SortOrder PERSON_ID = new SortOrder() { public int compare(Person p1, Person p2) { return Integer.valueOf(p1.getId()).compareTo(p2.getId()); } }; public static SortOrder PERSON_FIRST_NAME = new SortOrder() { public int compare(Person p1, Person p2) { return p1.getFirstName().compareTo(p2.getFirstName()); } }; public static SortOrder PERSON_SECOND_NAME = new SortOrder() { public int compare(Person p1, Person p2) { return p1.getSecondName().compareTo(p2.getSecondName()); } }; public static SortOrder invertOrder(final SortOrder toInvert) { return new SortOrder() { public int compare(Person p1, Person p2) { return -1 * toInvert.compare(p1, p2); } }; } public static Comparator combineSortOrders(final SortOrder... multipleSortOrders) { return new Comparator() { public int compare(Person p1, Person p2) { for (SortOrder personComparator: multipleSortOrders) { int result = personComparator.compare(p1, p2); if (result != 0) { return result; } } return 0; } }; } } public int getId() { return id; } public String getFirstName() { return firstName; } public String getSecondName() { return secondName; } @Override public String toString() { StringBuilder result = new StringBuilder(); result.append("Person with id: "); result.append(id); result.append(" and firstName: "); result.append(firstName); result.append(" and secondName: "); result.append(secondName); result.append("."); return result.toString(); } } 

Un esempio per usare la class Person e il suo SortOrder:

 import static multiplesortorder.Person.SortOrder.*; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import multiplesortorder.Person; public class Application { public static void main(String[] args) { List listPersons = new ArrayList(Arrays.asList( new Person(0, "...", "..."), new Person(1, "...", "...") )); Collections.sort(listPersons, combineSortOrders(PERSON_FIRST_NAME, invertOrder(PERSON_ID))); for (Person p: listPersons) { System.out.println(p.toString()); } } } 

oRUMOo

Recentemente ho scritto un Comparatore per ordinare più campi all’interno di un record String delimitato. Permette di definire il delimitatore, la struttura del record e le regole di ordinamento (alcune delle quali sono specifiche per tipo). È ansible utilizzare questo convertendo un record Persona in una stringa delimitata.

Le informazioni richieste vengono inoltrate al Comparatore stesso, a livello di codice o tramite un file XML.

XML è convalidato da un file XSD incorporato nel pacchetto. Ad esempio, di seguito è riportato un layout delimitato da tabulazioni con quattro campi (due dei quali sono ordinabili):

   	  Column One   Column Two   Column Three 2 true false true   Column Four 1 true true true yyyy-MM-dd   

Dovresti quindi usare questo in java in questo modo:

 Comparator comparator = new RowComparator( new XMLStructureReader(new File("layout.xml"))); 

La biblioteca può essere trovata qui:

http://sourceforge.net/projects/multicolumnrowcomparator/

Supponiamo che una Coordinate class sia presente e che sia necessario ordinarla in entrambi i modi in base alla coordinata X e alla coordinata Y. Per questo sono necessari due comparatori di differnet. Di seguito è il campione

 class Coordinate { int x,y; public Coordinate(int x, int y) { this.x = x; this.y = y; } static Comparator getCoordinateXComparator() { return new Comparator() { @Override public int compare(Coordinate Coordinate1, Coordinate Coordinate2) { if(Coordinate1.x < Coordinate2.x) return 1; else return 0; } // compare using Coordinate x }; } static Comparator getCoordinateYComparator() { return new Comparator() { @Override public int compare(Coordinate Coordinate1, Coordinate Coordinate2) { if(Coordinate1.y < Coordinate2.y) return 1; else return 0; } // compare using Coordinate y }; } }