183 votes

Comment utiliser le comparateur 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 pense que cela a quelque chose à voir avec le moulage dans la méthode de comparaison, mais j'ai joué avec et je n'ai toujours pas trouvé la solution.

1 votes

N'utilisez pas de types bruts dans le nouveau code stackoverflow.com/questions/2770321/ ; utiliser Comparator<People> , Comparable<People> , List<People> etc.

0 votes

J'ai changé le Comparator<People> mais quand je change Arraylist<People>, le Collections.sort obtient une erreur

222voto

Bart Kiers Points 79069

Il y a un certain nombre de choses gênantes avec votre exemple de classe :

  • il s'appelle People alors qu'il a un price y info (plutôt quelque chose pour les objets, pas les personnes) ;
  • lorsqu'on nomme une classe au 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émo de l'utilisation d'une 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);
    }
}

10 votes

Le comparateur d'âge et d'autres comparaisons similaires d'entiers peuvent être simplifiés en retournant les données suivantes a.age - b.age

13 votes

@Esko : l'astuce de la comparaison par soustraction ne fonctionne pas en général. int stackoverflow.com/questions/2728793/

1 votes

@Esko : à cause de ce que polygenelubricants a mentionné, je fais simplement toujours comme ça, même si pour un âge (qui ne deviendra pas très grand) la soustraction comme vous le mentionnez ferait l'affaire.

183voto

android developer Points 20939

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 (meaning the order stays the same)
     }
 });

Si vous avez du mal à vous en souvenir, essayez de vous rappeler qu'il est similaire (en termes de signe du nombre) à.. :

 lhs-rhs 

C'est dans le cas où vous voulez trier par ordre croissant : du plus petit nombre au plus grand nombre.

3 votes

@40Plot, ceux-ci sont pour le positionnement, imaginez une règle ou un axe avec eux.

0 votes

@Développeur Android , Merci d'avoir mentionné l'astuce pour se souvenir de la valeur à retourner pour chaque commande :)

0 votes

Meilleure explication de compare() jamais.

39voto

polygenelubricants Points 136838

Utilisez People implements Comparable<People> à la place ; cela définit l'ordre naturel pour People .

A Comparator<People> peuvent également être définis 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)
    • Sorts Comparable les objets en utilisant leur ordre naturel
  • <T> void sort(List<T> list, Comparator<? super T> c)
    • Trie tout ce que tu veux en utilisant une méthode compatible Comparator

Vous confondez les deux en essayant de trier une Comparator (c'est encore une fois pourquoi il n'est pas logique que Person implements Comparator<Person> ). Encore une fois, pour utiliser Collections.sort vous avez besoin d'une de ces choses pour être vrai :

  • Le type doit être Comparable (utilisez l'option 1arg sort )
  • A Comparator pour le type doit être fourni (utilisez la méthode 2-args sort )

Questions connexes


Aussi, ne pas utiliser les types bruts dans le nouveau code . Les types bruts ne sont pas sûrs, et ils ne sont fournis que pour des raisons de compatibilité.

C'est-à-dire, au lieu de ça :

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

vous auriez dû utiliser la déclaration générique sécurisée 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 n'est pas implements Comparable<Person> ), mais parce que vous avez utilisé un type brut, le compilateur ne l'a pas vérifié. et à la place, vous obtenez un ClassCastException au moment de l'exécution ! !!

Cela devrait vous convaincre de toujours utiliser des types génériques sûrs dans le nouveau code. Toujours.

Voir aussi

0 votes

L'explication de comparateur vs comparable est très utile

21voto

NumberFour Points 876

Par souci d'exhaustivité, voici une simple ligne simple compare méthode :

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

14voto

rince Points 962

Java 8 a ajouté une nouvelle façon de créer des comparateurs qui réduit la quantité de code que vous devez écrire, Comparateur.comparant . Consultez également Comparateur.inversé

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

}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X