Comment utiliser Comparator en Java pour sortinger

J’ai appris à utiliser le comparable mais j’ai des difficultés avec le comparateur. J’ai une erreur dans mon code:

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) 

Voici mon code:

 import java.util.Comparator; public class People implements Comparator { private int id; private Ssortingng info; private double price; public People(int newid, Ssortingng newinfo, double newprice) { setid(newid); setinfo(newinfo); setprice(newprice); } public int getid() { return id; } public void setid(int id) { this.id = id; } public Ssortingng getinfo() { return info; } public void setinfo(Ssortingng 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(Ssortingng[] 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)); } } } 

Je crois qu’il faut faire quelque chose avec le casting dans la méthode de comparaison mais je jouais avec et je ne pouvais toujours pas trouver la solution

Il y a quelques choses embarrassantes avec votre exemple de classe:

  • il s’appelle People alors qu’il a un price et des info (plus de quelque chose pour les objects, pas les gens);
  • en nommant une classe comme un pluriel, cela suggère que c’est une abstraction de plus d’une chose.

Quoi qu’il en soit, voici une démonstration de l’utilisation d’un Comparator :

 public class ComparatorDemo { public static void main(Ssortingng[] 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); } } 

MODIFIER

Et une démo Java 8 équivalente ressemblerait à ceci:

 public class ComparatorDemo { public static void main(Ssortingng[] 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); } } 

Voici un modèle super court pour faire le sorting tout de suite:

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

si c’est difficile à retenir, essayez de vous rappeler que c’est similaire (en termes de signe du nombre) à:

  lhs-rhs 

C’est dans le cas où vous voulez sortinger dans l’ordre croissant: du plus petit nombre au plus grand nombre.

Utiliser People implements Comparable ; cela définit l’ordre naturel pour les People .

Un Comparator peut également être défini, mais People implements Comparator n’est pas la bonne façon de faire les choses.

Les deux surcharges pour Collections.sort sont différentes:

  • > void sort(List list)
    • Trie des objects Comparable utilisant leur ordre naturel
  • void sort(List list, Comparator c)
    • Trie quelque chose en utilisant un Comparator compatible

Vous confondez les deux en essayant de sortinger un Comparator (ce qui est encore une fois la raison pour laquelle cela n’a pas de sens que Person implements Comparator ). Encore une fois, pour utiliser Collections.sort , l’une de ces valeurs doit être vraie:

  • Le type doit être Comparable (utilisez le sort 1-arg)
  • Un Comparator pour le type doit être fourni (utilisez le sort 2 arguments)

Questions connexes

  • Quand utiliser Comparable vs Comparator
  • Trier un tableauListe de contacts

De même, n’utilisez pas les types bruts dans le nouveau code . Les types bruts sont dangereux et ne sont fournis que pour la compatibilité.

C’est, au lieu de cela:

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

vous devriez avoir utilisé la déclaration générique typeafe comme ceci:

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

Vous constaterez alors que votre code ne comstack même pas !! Ce serait une bonne chose, car il y a quelque chose qui ne va pas avec le code ( Person n’implémente pas implements Comparable ), mais parce que vous avez utilisé du type brut, le compilateur ne l’a pas vérifié. -temps!!!

Cela devrait vous convaincre de toujours utiliser les types génériques typesafe dans le nouveau code. Toujours.

Voir également

  • Qu’est-ce qu’un type brut et pourquoi ne devrions-nous pas l’utiliser?

Par souci d’exhaustivité, voici une méthode de compare simple:

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

Java 8 a ajouté une nouvelle méthode de création de comparateurs qui réduit la quantité de code à écrire, Comparator.comparing . Consultez également Comparator.reversed

Voici un exemple

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

Vous souhaitez implémenter Comparable, pas Comparator. Vous devez implémenter la méthode compareTo. Vous êtes proche cependant. Le comparateur est une routine de comparaison “tierce”. Comparable est que cet object peut être comparé à un autre.

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

Notez que vous pouvez rechercher des valeurs nulles ici pour getId..just au cas où.

Voici un exemple de comparateur qui fonctionnera pour toute méthode d’argument zéro qui renvoie un comparable. Est-ce que quelque chose comme ça existe dans un jdk ou une bibliothèque?

 import java.lang.reflect.Method; import java.util.Comparator; public class NamedMethodComparator implements Comparator { // // instance variables // private Ssortingng methodName; private boolean isAsc; // // constructor // public NamedMethodComparator(Ssortingng 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, Ssortingng methodName) { Method method = getMethod(obj, methodName); Comparable comp = getValue(obj, method); return comp; } private Method getMethod(Object obj, Ssortingng 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 solution peut être optimisée de la façon suivante: Premièrement, utilisez une classe interne privée car la scope des champs doit être la classe englobante TestPeople, de sorte que l’implémentation de la classe People ne soit pas exposée au monde extérieur. Cela peut être compris en termes de création d’une API qui attend une liste de personnes sortingées Deuxièmement, en utilisant l’expression de Lamba (java 8) qui réduit le code, donc l’effort de développement

Le code serait donc comme ci-dessous:

 import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; public class TestPeople { public static void main(Ssortingng[] 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.toSsortingng())); } private class People { private int id; @Override public Ssortingng toSsortingng() { return "People [id=" + id + ", info=" + info + ", price=" + price + "]"; } private Ssortingng info; private double price; public People(int newid, Ssortingng newinfo, double newprice) { setid(newid); setinfo(newinfo); setprice(newprice); } public int getid() { return id; } public void setid(int id) { this.id = id; } public Ssortingng getinfo() { return info; } public void setinfo(Ssortingng info) { this.info = info; } public double getprice() { return price; } public void setprice(double price) { this.price = price; } } } 

Vous devez utiliser la méthode de sorting surchargé (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(Ssortingng[] 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; } } } }