318 votes

La meilleure façon de comparer des objets selon plusieurs champs?

Supposons que vous disposez d'un certain nombre d'objets qui ont plusieurs champs, ils peuvent être comparés par:

public class Person {

    private String firstName;
    private String lastName;
    private String age;

    /* Constructors */

    /* Methods */

}

Donc, dans cet exemple, lorsque vous demandez si:

a.compareTo(b) > 0

vous demandez peut-être si un nom de famille vient avant b, ou si a est plus grand que b, etc...

Quelle est la façon la plus propre à permettre à de multiples comparaison entre ces types d'objets sans ajouter inutiles ou les frais généraux?

  • java.lang.Interface Comparable permet de comparer en un seul champ
  • L'ajout de nombreux comparer les méthodes (c'est à dire compareByFirstName(), compareByAge(), etc...) est encombré, à mon avis.

Alors, quelle est la meilleure façon d'aller à ce sujet?

183voto

Steve Kuo Points 15196

Vous devez implémenter Compareable<Person>. En supposant que tous les champs ne seront pas null (par souci de simplicité), que l'âge est un int, et de comparer le classement est, prénom, l'âge, l' compareTo méthode est assez simple:

public int compareTo(Person p1, Person p2)
{
    int i = p1.firstName.compareTo(p2.firstName);
    if (i != 0) return i;

    i = p2.lastName.compareTo(p2.lastName);
    if (i != 0) return i;

    return Integer.valueOf(p1.age).compareTo(Integer.valueOf(p2.age);
}

132voto

Plantface Points 863

(à partir de la Maison de Code)

Malpropre et alambiqué: le Tri à la main

Collections.sort(pizzas, new Comparator<Pizza>() {  
    @Override  
    public int compare(Pizza p1, Pizza p2) {  
        int sizeCmp = p1.size.compareTo(p2.size);  
        if (sizeCmp != 0) {  
            return sizeCmp;  
        }  
        int nrOfToppingsCmp = p1.nrOfToppings.compareTo(p2.nrOfToppings);  
        if (nrOfToppingsCmp != 0) {  
            return nrOfToppingsCmp;  
        }  
        return p1.name.compareTo(p2.name);  
    }  
});  

Cela nécessite beaucoup de dactylographie, de l'entretien et est sujette aux erreurs.

L'réfléchie: Tri avec BeanComparator

Collections.sort(pizzas, new BeanComparator("size", new BeanComparator("nrOfToppings", new BeanComparator("name"))));  

Évidemment, ce n'est plus concis, mais encore plus les risques d'erreurs que vous perdez votre référence directe à la champs à l'aide des Cordes à la place. Maintenant, si un champ est renommé, le compilateur n'a même pas de signaler un problème. En outre, parce que cette solution utilise la réflexion, le tri est beaucoup plus lent.

Comment y arriver: Tri avec Google Goyave est ComparisonChain

Collections.sort(pizzas, new Comparator<Pizza>() {  
    @Override  
    public int compare(Pizza p1, Pizza p2) {  
        return ComparisonChain.start().compare(p1.size, p2.size).compare(p1.nrOfToppings, p2.nrOfToppings).compare(p1.name, p2.name).result();  
        // or in case the fields can be null:  
        /* 
        return ComparisonChain.start() 
           .compare(p1.size, p2.size, Ordering.natural().nullsLast()) 
           .compare(p1.nrOfToppings, p2.nrOfToppings, Ordering.natural().nullsLast()) 
           .compare(p1.name, p2.name, Ordering.natural().nullsLast()) 
           .result(); 
        */  
    }  
});  

C'est beaucoup mieux, mais nécessite une chaudière plaque de code pour la plupart des cas d'utilisation: null-valeurs de valeurs par défaut. Pour les nuls-champs, vous devez fournir une directive de Goyave que faire dans ce cas. C'est un mécanisme souple si vous voulez faire quelque chose de spécifique, mais souvent vous voulez le cas par défaut (ie. 1, a, b, z, null).

Tri avec Apache Commons CompareToBuilder

Collections.sort(pizzas, new Comparator<Pizza>() {  
    @Override  
    public int compare(Pizza p1, Pizza p2) {  
        return new CompareToBuilder().append(p1.size, p2.size).append(p1.nrOfToppings, p2.nrOfToppings).append(p1.name, p2.name).toComparison();  
    }  
});  

Comme la Goyave est ComparisonChain, cette bibliothèque de classe sortes facilement sur plusieurs champs, mais aussi définit le comportement par défaut pour les valeurs null (c'est à dire. 1, a, b, z, null). Cependant, vous ne pouvez pas spécifier autre chose, sauf si vous fournissez votre propre Comparateur.

Ainsi

En fin de compte, il revient à la saveur et le besoin de flexibilité (Goyave est ComparisonChain) vs code concis (Apache CompareToBuilder).

90voto

Elie Points 7628

Vous pouvez écrire un comparateur qui compare deux objets de la Personne, et vous pouvez examiner autant de champs que vous le souhaitez. Vous pouvez le mettre dans une variable dans votre comparateur que le dit le champ de comparer, même s'il serait sans doute plus simple pour écrire plusieurs comparateurs.

23voto

Nigel_V_Thomas Points 337

@Patrick Pour trier plus d'un terrain consécutivement essayer ComparatorChain

Un ComparatorChain est un Comparateur qui encapsule un ou plusieurs Comparateurs en séquence. Le ComparatorChain appels chaque Comparateur dans la séquence jusqu'à ce que 1) tout seul élément de Comparaison renvoie un résultat non nul (et que le résultat est ensuite retourné), ou 2) la ComparatorChain est épuisé (et le zéro est renvoyé). Ce type de tri est très similaire à multi-colonne de tri en SQL, et cette classe permet de classes Java à imiter ce genre de comportement lors du tri d'une Liste.

Pour faciliter davantage SQL-comme le tri, l'ordonnance d'un seul élément de Comparaison dans la liste peut >être inversé.

Appel d'une méthode qui ajoute de nouveaux éléments de comparaison ou de modifications de la montée/descente de tri après compare(Object, Object) a été appelé entraînera une UnsupportedOperationException. Cependant, prenez soin de ne pas modifier la Liste sous-jacente des éléments de comparaison ou de la BitSet qui définit l'ordre de tri.

Les Instances de ComparatorChain ne sont pas synchronisés. La classe n'est pas thread-safe au moment de la construction, mais il n'est pas thread-safe pour effectuer des comparaisons multiples, après toutes les opérations de configuration sont complètes.

13voto

Boune Points 625

Vous pouvez également jeter un oeil à Enum qui implémente Comparateur.

http://tobega.blogspot.com/2008/05/beautiful-enums.html

par exemple Les Collections.de tri(myChildren, Enfant.Ordre.ByAge.décroissant());

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