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

Author: Mr. Polywhirl, 2010-05-15

10 answers

Il y a quelques choses gênantes avec votre classe d'exemple:

  • il s'appelle People alors qu'il a un price et info (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);
    }
}
 187
Author: Bart Kiers, 2016-02-27 21:20:57

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.

 118
Author: android developer, 2017-01-19 08:19:29

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
  • <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-arg sort)
  • Un Comparator pour le type doit être fourni (utilisez les 2-args sort)

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

 32
Author: polygenelubricants, 2017-05-23 12:34:50

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());  
    }
}
 9
Author: 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, 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;
        }
    }



}
 6
Author: rince, 2017-03-09 19:44:28

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

 4
Author: Will Hartung, 2010-05-15 06:29:45

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

}
 2
Author: 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
Author: QuadBiker, 2015-10-06 06:08:34

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

, 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;
               }
            }
       }
    }
 0
Author: michal, 2015-07-26 13:45:03