131 votes

Tri d'un tableau d'objets à l'aide d'un ordre de tri personnalisé

Je cherche à mettre en œuvre une fonction de tri pour mon application de carnet d'adresses.

Je veux trier un ArrayList<Contact> contactArray . Contact est une classe qui contient quatre champs : nom, numéro de téléphone fixe, numéro de téléphone mobile et adresse. Je veux trier sur name .

Comment puis-je écrire une fonction de tri personnalisée pour faire cela ?

279voto

BalusC Points 498232

Voici un tutoriel sur le classement des objets :

Même si je vais donner quelques exemples, je vous recommande de le lire quand même.


Il existe plusieurs façons de trier un ArrayList . Si vous voulez définir un naturel (par défaut) commander alors vous devez laisser Contact mettre en œuvre Comparable . En supposant que vous voulez trier par défaut sur name Alors, faites (les vérifications nulles sont omises pour simplifier) :

public class Contact implements Comparable<Contact> {

    private String name;
    private String phone;
    private Address address;

    @Override
    public int compareTo(Contact other) {
        return name.compareTo(other.name);
    }

    // Add/generate getters/setters and other boilerplate.
}

afin que vous puissiez simplement faire

List<Contact> contacts = new ArrayList<Contact>();
// Fill it.

Collections.sort(contacts);

Si vous voulez définir un commande externe contrôlable (qui remplace l'ordre naturel), vous devez alors créer un fichier de type Comparator :

List<Contact> contacts = new ArrayList<Contact>();
// Fill it.

// Now sort by address instead of name (default).
Collections.sort(contacts, new Comparator<Contact>() {
    public int compare(Contact one, Contact other) {
        return one.getAddress().compareTo(other.getAddress());
    }
}); 

Vous pouvez même définir le Comparator dans le Contact afin que vous puissiez les réutiliser au lieu de les recréer à chaque fois :

public class Contact {

    private String name;
    private String phone;
    private Address address;

    // ...

    public static Comparator<Contact> COMPARE_BY_PHONE = new Comparator<Contact>() {
        public int compare(Contact one, Contact other) {
            return one.phone.compareTo(other.phone);
        }
    };

    public static Comparator<Contact> COMPARE_BY_ADDRESS = new Comparator<Contact>() {
        public int compare(Contact one, Contact other) {
            return one.address.compareTo(other.address);
        }
    };

}

qui peut être utilisé comme suit :

List<Contact> contacts = new ArrayList<Contact>();
// Fill it.

// Sort by address.
Collections.sort(contacts, Contact.COMPARE_BY_ADDRESS);

// Sort later by phone.
Collections.sort(contacts, Contact.COMPARE_BY_PHONE);

Et pour couronner le tout, vous pourriez envisager d'utiliser une comparateur générique de type javabean :

public class BeanComparator implements Comparator<Object> {

    private String getter;

    public BeanComparator(String field) {
        this.getter = "get" + field.substring(0, 1).toUpperCase() + field.substring(1);
    }

    public int compare(Object o1, Object o2) {
        try {
            if (o1 != null && o2 != null) {
                o1 = o1.getClass().getMethod(getter, new Class[0]).invoke(o1, new Object[0]);
                o2 = o2.getClass().getMethod(getter, new Class[0]).invoke(o2, new Object[0]);
            }
        } catch (Exception e) {
            // If this exception occurs, then it is usually a fault of the developer.
            throw new RuntimeException("Cannot compare " + o1 + " with " + o2 + " on " + getter, e);
        }

        return (o1 == null) ? -1 : ((o2 == null) ? 1 : ((Comparable<Object>) o1).compareTo(o2));
    }

}

que vous pouvez utiliser comme suit :

// Sort on "phone" field of the Contact bean.
Collections.sort(contacts, new BeanComparator("phone"));

(comme vous le voyez dans le code, les champs éventuellement nuls sont déjà couverts pour éviter les NPE lors du tri).

2 votes

J'ajouterais la possibilité de prédéfinir plusieurs comparateurs, puis de les utiliser par leur nom...

2 votes

En fait, je viens de le faire. C'est plus facile que d'essayer de m'expliquer.

0 votes

@BalusC : No probs. Je ne peux pas m'attribuer le mérite de l'idée, je l'ai eu de String.CASE_INSENSITIVE_ORDER et des amis, mais j'aime ça. Le code résultant est plus facile à lire.

8voto

bguiz Points 6226

Cette page vous dit tout ce que vous devez savoir sur le tri des collections, comme ArrayList.

En gros, vous devez

  • faites votre Contact mettent en œuvre la classe Comparable interface par
    • création d'une méthode public int compareTo(Contact anotherContact) en son sein.
  • Une fois que vous avez fait cela, vous pouvez simplement appeler Collections.sort(myContactList); ,
    • myContactList est ArrayList<Contact> (ou toute autre collection de Contact ).

Il existe également une autre méthode, qui consiste à créer une classe Comparator, et vous pouvez lire ce qui suit sur la page liée.

Exemple :

public class Contact implements Comparable<Contact> {

    ....

    //return -1 for less than, 0 for equals, and 1 for more than
    public compareTo(Contact anotherContact) {
        int result = 0;
        result = getName().compareTo(anotherContact.getName());
        if (result != 0)
        {
            return result;
        }
        result = getNunmber().compareTo(anotherContact.getNumber());
        if (result != 0)
        {
            return result;
        }
        ...
    }
}

5voto

Etienne Neveu Points 7454

BalusC et bguiz ont déjà donné des réponses très complètes sur la façon d'utiliser les comparateurs intégrés de Java.

Je veux juste ajouter que google-collections a une Commander qui est plus "puissante" que les comparateurs standard. Cela peut valoir le coup de jeter un coup d'oeil. Vous pouvez faire des choses sympas comme composer des ordres, les inverser, les ordonner en fonction du résultat d'une fonction pour vos objets...

Ici est un article de blog qui mentionne certains de ses avantages.

0 votes

Notez que google-collections fait désormais partie de Guava (les bibliothèques java communes de Google). Il se peut donc que vous souhaitiez dépendre de Guava (ou du module de collection de Guava) si vous souhaitez utiliser la classe Ordering.

4voto

Kaleb Brasee Points 25776

Vous devez faire en sorte que vos classes de contact implémentent Comparable et ensuite mettre en œuvre le compareTo(Contact) méthode. De cette façon, Collections.sort pourra les trier pour vous. D'après la page dont j'ai donné le lien, compareTo "renvoie un nombre entier négatif, zéro ou un nombre entier positif si cet objet est inférieur, égal ou supérieur à l'objet spécifié".

Par exemple, si vous vouliez trier par nom (de A à Z), votre classe ressemblerait à ceci :

public class Contact implements Comparable<Contact> {

    private String name;

    // all the other attributes and methods

    public compareTo(Contact other) {
        return this.name.compareTo(other.name);
    }
}

0 votes

Cela a bien fonctionné pour moi, merci ! J'ai également utilisé compareToIgnoreCase pour ignorer la casse.

3voto

Mario Fusco Points 4163

En utilisant lambdaj vous pouvez trier une collection de vos contacts (par exemple par leur nom) comme suit

sort(contacts, on(Contact.class).getName());

ou par leur adresse :

sort(contacts, on(Contacts.class).getAddress());

et ainsi de suite. Plus généralement, il offre un DSL pour accéder à vos collections et les manipuler de nombreuses façons, comme filtrer ou grouper vos contacts en fonction de certaines conditions, agréger certaines de leurs valeurs de propriété, etc.

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