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

137
demandé sur Mr. Polywhirl 2010-05-15 10:22:07

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 et info (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);
    }
}
187
répondu Bart Kiers 2016-02-27 21:20:57

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.

118
répondu android developer 2017-01-19 08:19:29

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
  • <T> void sort(List<T> list, Comparator<? super T> c)
    • trie tout ce qui utilise un compatible Comparator

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-arg sort )
  • A Comparator pour le type doit être fournie (par l'utilisation de la 2-args sort )

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

32
répondu polygenelubricants 2017-05-23 12:34:50

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());  
    }
}
9
répondu NumberFour 2014-12-10 19:30:57

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



}
6
répondu rince 2017-03-09 19:44:28

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ù.

4
répondu Will Hartung 2010-05-15 06:29:45

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

}
2
répondu John 2017-03-10 14:41:12
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;
            }
        };
1
répondu QuadBiker 2015-10-06 06:08:34

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;
        }
    }
}
1
répondu Akhil Gupta 2016-11-24 15:39:38

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;
               }
            }
       }
    }
0
répondu michal 2015-07-26 13:45:03