Comment utiliser Comparator en Java pour trier
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 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 qu'il doit faire quelque chose avec le casting dans la méthode de comparaison, mais je jouais avec elle et ne pouvait toujours pas trouver la solution
10 réponses
il y a un couple de choses bizarres avec votre classe d'exemple:
- on l'appelle les gens alors qu'il a un
price
etinfo
(plus quelque chose pour les objets, pas les gens); - en désignant une classe comme étant le pluriel de quelque chose, elle suggère qu'il s'agit d'une abstraction de plus d'une chose.
quoi qu'il en soit, voici une démo de comment utiliser 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);
}
}
EDIT
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 très 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
}
});
si c'est dur à 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 trier dans l'ordre croissant : du plus petit nombre au plus grand nombre.
utiliser People implements Comparable<People>
à la place; Cela définit l'ordre naturel pour People
.
Un Comparator<People>
peut aussi être défini en plus, mais People implements Comparator<People>
n'est pas la bonne façon de faire les choses.
les deux surcharges pour Collections.sort
sont différentes:
-
<T extends Comparable<? super T>> void sort(List<T> list)
- Sortes
Comparable
objets à l'aide de leur ordre naturel
- Sortes
-
<T> void sort(List<T> list, Comparator<? super T> c)
- trie tout ce qui utilise un compatible
Comparator
- trie tout ce qui utilise un compatible
vous confondez les deux en essayant de trier un Comparator
(ce qui est encore une fois pourquoi il ne fait pas de sens que Person implements Comparator<Person>
). Encore une fois, pour utiliser Collections.sort
, vous avez besoin d'un de ceux-ci pour être vrai:
- le type doit être
Comparable
(utiliser le 1-argsort
) - A
Comparator
pour le type doit être fournie (par l'utilisation de la 2-argssort
)
questions connexes
en outre, ne pas utiliser les types bruts dans le nouveau code . Les types crus sont dangereux, et il n'est fourni que 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 de typesafe comme ceci:
List<People> peps = new ArrayList<People>(); // GOOD!!!
vous trouverez alors que votre code ne compile même pas!! ce serait une bonne chose, parce qu'il y a quelque chose qui ne va pas avec le code ( Person
ne fait pas implements Comparable<Person>
), mais parce que vous avez utilisé le type brut, le compilateur n'a pas vérifié pour ce , et à la place vous obtenez un ClassCastException
à l'exécution!!!
cela devrait vous convaincre d'utiliser toujours les types génériques de typesafe dans le nouveau code. Toujours.
Voir aussi
par souci d'exhaustivité, voici une méthode simple à une seule doublure 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, comparateur.comparer . Vérifier aussi comparateur.inversé
voici un échantillon
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 mettre en œuvre Comparable, pas de comparaison. Vous devez mettre en œuvre la méthode compareTo. Vous êtes à proximité. Le comparateur est une méthode de comparaison de "tierce partie". 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;
}
Note, Vous pouvez vérifier les nulls ici pour getId..juste au cas où.
voici un exemple d'un comparateur qui fonctionnera pour n'importe quelle méthode arg zéro qui retourne 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<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, utiliser une classe intérieure privée que la marge de manœuvre pour les champs est d'être les personnes de test de classe enfermer afin que la mise en œuvre de la classe des gens ne seront pas exposés au monde extérieur. Cela peut être compris en termes de création D'un APIthat attend une liste triée des personnes Deuxièmement, en utilisant L'expression Lamba (java 8) qui réduit le code, donc l'effort de développement
donc 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 méthode de tri surchargée (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<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;
}
}
}
}