Comment utiliser le comparateur en Java pour trier
J'ai appris à utiliser le comparable mais j'ai des difficultés avec le comparateur. Je vais avoir 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 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));
}
}
}
Je crois que cela doit 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
10 answers
Il y a quelques choses gênantes avec votre classe d'exemple:
- il s'appelle People alors qu'il a un
price
etinfo
(plus quelque chose pour les objets, pas les personnes); - lorsque l'on nomme une classe comme un pluriel de quelque chose, cela suggère qu'il s'agit d'une abstraction de plus d'une chose.
Quoi qu'il en soit, voici une démonstration de l'utilisation d'un Comparator<T>
:
public class ComparatorDemo {
public static void main(String[] args) {
List<Person> 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<Person> {
@Override
public int compare(Person a, Person b) {
return a.name.compareToIgnoreCase(b.name);
}
}
class AgeComparator implements Comparator<Person> {
@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(String[] args) {
List<Person> 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 tri tout de suite:
Collections.sort(people,new Comparator<Person>(){
@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
}
});
S'il est difficile de se souvenir, essayez de vous rappeler que c'est similaire (en termes de signe du nombre) à:
lhs-rhs
C'est au cas où vous souhaitez trier par ordre croissant : du plus petit nombre au plus grand nombre.
Utilisez plutôt People implements Comparable<People>
; cela définit l'ordre naturel pour People
.
Un Comparator<People>
peut également être défini en plus, mais People implements Comparator<People>
n'est pas la bonne façon de faire les choses.
Les deux surcharges de Collections.sort
sont différentes:
-
<T extends Comparable<? super T>> void sort(List<T> list)
- Trie
Comparable
les objets en utilisant leur ordre naturel
- Trie
-
<T> void sort(List<T> list, Comparator<? super T> c)
- trie tout en utilisant un {[10 compatible]}
Vous confondez les deux en essayant de trier un Comparator
(ce qui est encore une fois pourquoi cela n'a pas de sens que Person implements Comparator<Person>
). Encore une fois, pour utiliser Collections.sort
, vous avez besoin que l'un d'entre eux soit vrai:
- Le type doit être
Comparable
(utilisez le 1-argsort
) - Un
Comparator
pour le type doit être fourni (utilisez les 2-argssort
)
Questions connexes
De plus, n'utilisez pas de types bruts dans le nouveau code. Brut les types sont dangereux, et il est fourni uniquement pour la compatibilité.
C'est-à-dire, au lieu de cela:
ArrayList peps = new ArrayList(); // BAD!!! No generic safety!
Vous auriez dû utiliser la déclaration générique typesafe comme ceci:
List<People> peps = new ArrayList<People>(); // GOOD!!!
Vous constaterez alors que votre code ne compile même pas!! Ce serait une bonne chose, car il y a quelque chose qui ne va pas avec le code (Person
ne le fait pas implements Comparable<Person>
), mais parce que vous avez utilisé le type raw, le compilateur n'a pas vérifié ce , et à la place vous obtenez un ClassCastException
à au moment de l'exécution!!!
Cela devrait vous convaincre de toujours utiliser les types génériques typesafe dans le nouveau code. Toujours.
Voir aussi
Par souci d'exhaustivité, voici une méthode simple à une ligne compare
:
Collections.sort(people,new Comparator<Person>()
{
@Override
public int compare(Person lhs,Person rhs)
{
return Integer.signum(lhs.getId()-rhs.getId());
}
}
Java 8 a ajouté une nouvelle façon de faire des comparateurs qui réduit la quantité de code que vous devez écrire, Comparator.comparaison . Consultez également Comparateur.inversé
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<Person> 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 voulez implémenter Comparable, pas Comparateur. Vous devez implémenter la méthode compareTo. Vous êtes proche cependant. Comparateur est une routine de comparaison "3rd party". Comparable est que cet objet 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;
}
Remarque, vous voudrez peut-être vérifier les valeurs nulles ici pour getId..juste au cas où.
Voici un exemple de comparateur qui fonctionnera pour toute méthode arg zéro qui renvoie un comparable. Quelque chose comme ça existe-t-il dans un jdk ou une bibliothèque?
import java.lang.reflect.Method;
import java.util.Comparator;
public class NamedMethodComparator implements Comparator<Object> {
//
// 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<JobSet> JobEndTimeComparator = new Comparator<JobSet>() {
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 manière suivante: Tout d'abord, utilisez une classe interne privée car la portée des champs doit être la classe englobante TestPeople afin que l'implémentation des gens de classe ne soit pas exposée au monde extérieur. Cela peut être compris en termes de création d'un APIthat attend une liste triée de personnes Deuxièmement, en utilisant l'expression Lamba (java 8) qui réduit le code, d'où l'effort de développement
Par conséquent, le code serait comme ci-dessous:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class TestPeople {
public static void main(String[] args) {
ArrayList<People> 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;
}
}
}
, Vous devez utiliser la surcharge de tri(ppe, de nouvelles Personnes()) méthode
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<People> 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<People>
{
@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;
}
}
}
}