Come utilizzare Comparator in Java per ordinare

Ho imparato a usare il comparabile ma ho difficoltà con il Comparatore. Ho un errore nel mio codice:

Exception in thread "main" java.lang.ClassCastException: New.People cannot be cast to java.lang.Comparable at java.util.Arrays.mergeSort(Unknown Source) at java.util.Arrays.sort(Unknown Source) at java.util.Collections.sort(Unknown Source) at New.TestPeople.main(TestPeople.java:18) 

Ecco il mio codice:

 import java.util.Comparator; public class People implements Comparator { private int id; private String info; private double price; public People(int newid, String newinfo, double newprice) { setid(newid); setinfo(newinfo); setprice(newprice); } public int getid() { return id; } public void setid(int id) { this.id = id; } public String getinfo() { return info; } public void setinfo(String info) { this.info = info; } public double getprice() { return price; } public void setprice(double price) { this.price = price; } public int compare(Object obj1, Object obj2) { Integer p1 = ((People) obj1).getid(); Integer p2 = ((People) obj2).getid(); if (p1 > p2) { return 1; } else if (p1 < p2){ return -1; } else { return 0; } } } 
 import java.util.ArrayList; import java.util.Collections; public class TestPeople { public static void main(String[] args) { ArrayList peps = new ArrayList(); peps.add(new People(123, "M", 14.25)); peps.add(new People(234, "M", 6.21)); peps.add(new People(362, "F", 9.23)); peps.add(new People(111, "M", 65.99)); peps.add(new People(535, "F", 9.23)); Collections.sort(peps); for (int i = 0; i < peps.size(); i++){ System.out.println(peps.get(i)); } } } 

Credo che debba fare qualcosa con il casting nel metodo di confronto, ma io stavo giocando con esso e ancora non riuscivo a trovare la soluzione

Ci sono un paio di cose scomode con la tua class di esempio:

  • si chiama People mentre ha un price e info (più qualcosa per oggetti, non persone);
  • quando si nomina una class come un plurale di qualcosa, si suggerisce che è un’astrazione di più di una cosa.

Ad ogni modo, ecco una demo di come usare un Comparator :

 public class ComparatorDemo { public static void main(String[] args) { List people = Arrays.asList( new Person("Joe", 24), new Person("Pete", 18), new Person("Chris", 21) ); Collections.sort(people, new LexicographicComparator()); System.out.println(people); Collections.sort(people, new AgeComparator()); System.out.println(people); } } class LexicographicComparator implements Comparator { @Override public int compare(Person a, Person b) { return a.name.compareToIgnoreCase(b.name); } } class AgeComparator implements Comparator { @Override public int compare(Person a, Person b) { return a.age < b.age ? -1 : a.age == b.age ? 0 : 1; } } class Person { String name; int age; Person(String n, int a) { name = n; age = a; } @Override public String toString() { return String.format("{name=%s, age=%d}", name, age); } } 

MODIFICARE

E una demo simile a Java 8 sarebbe simile a questa:

 public class ComparatorDemo { public static void main(String[] args) { List people = Arrays.asList( new Person("Joe", 24), new Person("Pete", 18), new Person("Chris", 21) ); Collections.sort(people, (a, b) -> a.name.compareToIgnoreCase(b.name)); System.out.println(people); Collections.sort(people, (a, b) -> a.age < b.age ? -1 : a.age == b.age ? 0 : 1); System.out.println(people); } } 

Ecco un modello super short per fare subito l’ordinamento:

 Collections.sort(people,new Comparator(){ @Override public int compare(final Person lhs,Person rhs) { //TODO return 1 if rhs should be before lhs // return -1 if lhs should be before rhs // return 0 otherwise } }); 

se è difficile da ricordare, prova a ricordare che è simile (in termini di segno del numero) a:

  lhs-rhs 

Nel caso in cui si desidera ordinare in ordine crescente: dal numero più piccolo al numero più grande.

Usa People implements Comparable invece People implements Comparable ; questo definisce l’ordinamento naturale per le People .

Inoltre, un Comparator può anche essere definito, ma le People implements Comparator non è il modo giusto di fare le cose.

I due overload per Collections.sort sono diversi:

  • > void sort(List list)
    • Ordina oggetti Comparable usando il loro ordinamento naturale
  • void sort(List list, Comparator< ? super T> c)
    • Ordina qualunque cosa usando un Comparator compatibile

Stai confondendo i due cercando di ordinare un Comparator (che è di nuovo il motivo per cui non ha senso che Person implements Comparator ). Ancora una volta, per utilizzare Collections.sort , è necessario che uno di questi sia vero:

  • Il tipo deve essere Comparable (usa l’ sort 1-arg)
  • Deve essere fornito un Comparator per il tipo (utilizzare l’ sort 2 arg)

Domande correlate

  • Quando utilizzare Comparable vs Comparator
  • Ordinamento di una lista array di contatti

Inoltre, non utilizzare i tipi non elaborati nel nuovo codice . I tipi grezzi non sono sicuri e vengono forniti solo per la compatibilità.

Cioè, invece di questo:

 ArrayList peps = new ArrayList(); // BAD!!! No generic safety! 

avresti dovuto usare la dichiarazione generica typesafe come questa:

 List peps = new ArrayList(); // GOOD!!! 

Scoprirai quindi che il tuo codice non viene nemmeno compilato !! Sarebbe una buona cosa, perché c’è qualcosa di sbagliato nel codice ( Person non implements Comparable ), ma poiché hai usato il tipo raw, il compilatore non ha verificato questo , e invece ottieni un ClassCastException in esecuzione -tempo!!!

Questo dovrebbe convincerti a usare sempre i tipi generici typesafe nel nuovo codice. Sempre.

Guarda anche

  • Che cos’è un tipo grezzo e perché non dovremmo usarlo?

Per completezza, ecco un semplice metodo di compare one-liner:

 Collections.sort(people,new Comparator() { @Override public int compare(Person lhs,Person rhs) { return Integer.signum(lhs.getId()-rhs.getId()); } } 

Java 8 ha aggiunto un nuovo modo di fare comparatori che riduce la quantità di codice che devi scrivere, Comparator.comparing . Controlla anche Comparator.reversed

Ecco un esempio

 import org.junit.Test; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import static org.junit.Assert.assertTrue; public class ComparatorTest { @Test public void test() { List peopleList = new ArrayList<>(); peopleList.add(new Person("A", 1000)); peopleList.add(new Person("B", 1)); peopleList.add(new Person("C", 50)); peopleList.add(new Person("Z", 500)); //sort by name, ascending peopleList.sort(Comparator.comparing(Person::getName)); assertTrue(peopleList.get(0).getName().equals("A")); assertTrue(peopleList.get(peopleList.size() - 1).getName().equals("Z")); //sort by name, descending peopleList.sort(Comparator.comparing(Person::getName).reversed()); assertTrue(peopleList.get(0).getName().equals("Z")); assertTrue(peopleList.get(peopleList.size() - 1).getName().equals("A")); //sort by age, ascending peopleList.sort(Comparator.comparing(Person::getAge)); assertTrue(peopleList.get(0).getAge() == 1); assertTrue(peopleList.get(peopleList.size() - 1).getAge() == 1000); //sort by age, descending peopleList.sort(Comparator.comparing(Person::getAge).reversed()); assertTrue(peopleList.get(0).getAge() == 1000); assertTrue(peopleList.get(peopleList.size() - 1).getAge() == 1); } class Person { String name; int age; Person(String n, int a) { name = n; age = a; } public String getName() { return name; } public int getAge() { return age; } public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } } } 

Vuoi implementare Comparable, non Comparator. È necessario implementare il metodo compareTo. Sei vicino però. Il comparatore è una routine di confronto di “terze parti”. Paragonabile è che questo object può essere confrontato con un altro.

 public int compareTo(Object obj1) { People that = (People)obj1; Integer p1 = this.getId(); Integer p2 = that.getid(); if (p1 > p2 ){ return 1; } else if (p1 < p2){ return -1; } else return 0; } 

Nota, potresti voler controllare i null qui per getId..per caso.

Ecco un esempio di un comparatore che funzionerà con qualsiasi metodo zero arg che restituisce un confronto. Esiste qualcosa del genere in un jdk o in una libreria?

 import java.lang.reflect.Method; import java.util.Comparator; public class NamedMethodComparator implements Comparator { // // instance variables // private String methodName; private boolean isAsc; // // constructor // public NamedMethodComparator(String methodName, boolean isAsc) { this.methodName = methodName; this.isAsc = isAsc; } /** * Method to compare two objects using the method named in the constructor. */ @Override public int compare(Object obj1, Object obj2) { Comparable comp1 = getValue(obj1, methodName); Comparable comp2 = getValue(obj2, methodName); if (isAsc) { return comp1.compareTo(comp2); } else { return comp2.compareTo(comp1); } } // // implementation // private Comparable getValue(Object obj, String methodName) { Method method = getMethod(obj, methodName); Comparable comp = getValue(obj, method); return comp; } private Method getMethod(Object obj, String methodName) { try { Class[] signature = {}; Method method = obj.getClass().getMethod(methodName, signature); return method; } catch (Exception exp) { throw new RuntimeException(exp); } } private Comparable getValue(Object obj, Method method) { Object[] args = {}; try { Object rtn = method.invoke(obj, args); Comparable comp = (Comparable) rtn; return comp; } catch (Exception exp) { throw new RuntimeException(exp); } } } 
 public static Comparator JobEndTimeComparator = new Comparator() { public int compare(JobSet j1, JobSet j2) { int cost1 = j1.cost; int cost2 = j2.cost; return cost1-cost2; } }; 

La soluzione può essere ottimizzata nel modo seguente: in primo luogo, utilizzare una class interna privata in quanto l’ambito dei campi deve essere la class che racchiude i TestPeople, così come l’implementazione della class Le persone non saranno esposte al mondo esterno. Questo può essere compreso in termini di creazione di un’API che si aspetta un elenco ordinato di persone In secondo luogo, utilizzando l’espressione Lamba (java 8) che riduce il codice, quindi lo sforzo di sviluppo

Quindi il codice sarebbe come di seguito:

 import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; public class TestPeople { public static void main(String[] args) { ArrayList peps = new ArrayList<>();// Be specific, to avoid // classCast Exception TestPeople test = new TestPeople(); peps.add(test.new People(123, "M", 14.25)); peps.add(test.new People(234, "M", 6.21)); peps.add(test.new People(362, "F", 9.23)); peps.add(test.new People(111, "M", 65.99)); peps.add(test.new People(535, "F", 9.23)); /* * Collections.sort(peps); * * for (int i = 0; i < peps.size(); i++){ * System.out.println(peps.get(i)); } */ // The above code can be replaced by followin: peps.sort((People p1, People p2) -> p1.getid() - p2.getid()); peps.forEach((p) -> System.out.println(" " + p.toString())); } private class People { private int id; @Override public String toString() { return "People [id=" + id + ", info=" + info + ", price=" + price + "]"; } private String info; private double price; public People(int newid, String newinfo, double newprice) { setid(newid); setinfo(newinfo); setprice(newprice); } public int getid() { return id; } public void setid(int id) { this.id = id; } public String getinfo() { return info; } public void setinfo(String info) { this.info = info; } public double getprice() { return price; } public void setprice(double price) { this.price = price; } } } 

È necessario utilizzare il metodo di ordinamento sovraccarico (peps, new People ())

 import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; public class Test { public static void main(String[] args) { List peps = new ArrayList<>(); peps.add(new People(123, "M", 14.25)); peps.add(new People(234, "M", 6.21)); peps.add(new People(362, "F", 9.23)); peps.add(new People(111, "M", 65.99)); peps.add(new People(535, "F", 9.23)); Collections.sort(peps, new People().new ComparatorId()); for (int i = 0; i < peps.size(); i++) { System.out.println(peps.get(i)); } } } class People { private int id; private String info; private double price; public People() { } public People(int newid, String newinfo, double newprice) { setid(newid); setinfo(newinfo); setprice(newprice); } public int getid() { return id; } public void setid(int id) { this.id = id; } public String getinfo() { return info; } public void setinfo(String info) { this.info = info; } public double getprice() { return price; } public void setprice(double price) { this.price = price; } class ComparatorId implements Comparator { @Override public int compare(People obj1, People obj2) { Integer p1 = obj1.getid(); Integer p2 = obj2.getid(); if (p1 > p2) { return 1; } else if (p1 < p2){ return -1; } else { return 0; } } } }