J'implémente une compareTo()
méthode pour une classe simple comme celle-ci (pour pouvoir utiliser Collections.sort()
et d'autres goodies offerts par la plate-forme Java):
public class Metadata implements Comparable<Metadata> {
private String name;
private String value;
// Imagine basic constructor and accessors here
// Irrelevant parts omitted
}
Je veux que l' ordre naturel de ces objets soit: 1) trié par nom et 2) trié par valeur si le nom est le même; les deux comparaisons doivent être insensibles à la casse. Pour les deux champs, les valeurs nulles sont parfaitement acceptables et compareTo
ne doivent donc pas être interrompues dans ces cas.
La solution qui me vient à l'esprit est la suivante (j'utilise ici des «clauses de garde», tandis que d'autres préfèrent peut-être un seul point de retour, mais ce n'est pas la question):
// primarily by name, secondarily by value; null-safe; case-insensitive
public int compareTo(Metadata other) {
if (this.name == null && other.name != null){
return -1;
}
else if (this.name != null && other.name == null){
return 1;
}
else if (this.name != null && other.name != null) {
int result = this.name.compareToIgnoreCase(other.name);
if (result != 0){
return result;
}
}
if (this.value == null) {
return other.value == null ? 0 : -1;
}
if (other.value == null){
return 1;
}
return this.value.compareToIgnoreCase(other.value);
}
Cela fait le travail, mais je ne suis pas parfaitement satisfait de ce code. Certes, ce n'est pas très complexe, mais assez verbeux et fastidieux.
La question est, comment feriez-vous cela moins verbeux (tout en conservant la fonctionnalité)? N'hésitez pas à vous référer aux bibliothèques standard Java ou à Apache Commons si elles vous aident. Est-ce que la seule option pour rendre cela (un peu) plus simple serait d'implémenter mon propre "NullSafeStringComparator" et de l'appliquer pour comparer les deux champs?
Modifications 1 à 3 : à droite d'Eddie; correction du cas "les deux noms sont nuls" ci-dessus
À propos de la réponse acceptée
J'ai posé cette question en 2009, sur Java 1.6 bien sûr, et à l'époque la solution JDK pure d'Eddie était ma réponse acceptée préférée. Je n'ai jamais eu l'occasion de changer cela jusqu'à maintenant (2017).
Il existe également des solutions de bibliothèque tierces - une Apache Commons Collections de 2009 et une 2013 Guava, toutes deux publiées par moi - que j'ai préférées à un moment donné.
J'ai maintenant fait le nettoyage solution Java 8 de Lukasz Wiktor la réponse acceptée. Cela devrait certainement être préféré si sur Java 8, et ces jours-ci, Java 8 devrait être disponible pour presque tous les projets.
Réponses:
Utilisation de Java 8 :
la source
Collections.sort(List)
ne fonctionne pas lorsque la liste contient des valeurs nulles, le commentaire n'est pas pertinent pour la question.Vous pouvez simplement utiliser Apache Commons Lang :
la source
nullsFirst()
/nullsLast()
.org.apache.commons.lang3
) est «hérité / mal entretenu / de mauvaise qualité» est fausse ou au mieux sans fondement. Commons Lang3 est facile à comprendre et à utiliser, et il est activement maintenu. C'est probablement ma bibliothèque la plus souvent utilisée (à part Spring Framework et Spring Security) - la classe StringUtils avec ses méthodes de sécurité null rend la normalisation d'entrée triviale, par exemple.J'implémenterais un comparateur sûr nul. Il peut y avoir une implémentation là-bas, mais c'est tellement simple à implémenter que j'ai toujours roulé la mienne.
Remarque: votre comparateur ci-dessus, si les deux noms sont nuls, ne comparera même pas les champs de valeur. Je ne pense pas que ce soit ce que tu veux.
Je mettrais en œuvre cela avec quelque chose comme ce qui suit:
EDIT: Correction des fautes de frappe dans l'exemple de code. C'est ce que j'obtiens si je ne l'ai pas testé en premier!
EDIT: promotion de nullSafeStringComparator en statique.
la source
final
mot-clé n'est pas vraiment nécessaire (le code Java est déjà verbeux tel quel.) Cependant, il empêche la réutilisation des paramètres en tant que variables locales (une pratique de codage terrible.) notre compréhension collective des logiciels s'améliore avec le temps, nous savons que les choses doivent être finales / const / inmuables par défaut. Je préfère donc un peu plus de verbosité dans l'utilisation desfinal
déclarations de paramètres (aussi triviale que soit la fonction) pour obtenirinmutability-by-quasi-default
.) La surcharge de compréhensibilité / maintenabilité est négligeable dans le grand schéma des choses.Voir le bas de cette réponse pour une solution mise à jour (2013) utilisant Guava.
C'est ce que j'ai finalement choisi. Il s'est avéré que nous disposions déjà d'une méthode utilitaire pour la comparaison de chaînes à valeur nulle, la solution la plus simple était donc de l'utiliser. (C'est une grosse base de code; il est facile de rater ce genre de chose :)
Voici comment l'assistant est défini (il est surchargé afin que vous puissiez également définir si les valeurs nulles viennent en premier ou en dernier, si vous le souhaitez):
C'est donc essentiellement la même chose que la réponse d'Eddie (même si je n'appellerais pas une méthode d'assistance statique un comparateur ) et celle d'uzhin aussi.
Quoi qu'il en soit, en général, j'aurais fortement favorisé la solution de Patrick , car je pense que c'est une bonne pratique d'utiliser autant que possible les bibliothèques établies. ( Connaissez et utilisez les bibliothèques comme le dit Josh Bloch.) Mais dans ce cas, cela n'aurait pas donné le code le plus propre et le plus simple.
Edit (2009): version Apache Commons Collections
En fait, voici un moyen de
NullComparator
simplifier la solution basée sur Apache Commons . Combinez-le avec le non sensible à la casseComparator
fourni enString
classe:Maintenant, c'est assez élégant, je pense. (Il reste un petit problème: les communes
NullComparator
ne prennent pas en charge les génériques, il y a donc une affectation non vérifiée.)Mise à jour (2013): version Guava
Près de 5 ans plus tard, voici comment j'aborderai ma question initiale. Si je codais en Java, j'utiliserais (bien sûr) Guava . (Et certainement pas Apache Commons.)
Mettez cette constante quelque part, par exemple dans la classe "StringUtils":
Puis, dans
public class Metadata implements Comparable<Metadata>
:Bien sûr, c'est presque identique à la version Apache Commons (les deux utilisent CASE_INSENSITIVE_ORDER de JDK ), l'utilisation
nullsLast()
étant la seule chose spécifique à Guava. Cette version est préférable simplement parce que Guava est préférable, en tant que dépendance, aux collections communes. (Comme tout le monde est d'accord .)Si vous vous posez des questions
Ordering
, notez qu'il met en œuvreComparator
. C'est assez pratique, en particulier pour les besoins de tri plus complexes, vous permettant par exemple d'enchaîner plusieurs commandes à l'aide decompound()
. Lisez la commande expliquée pour en savoir plus!la source
ComparatorChain
vous n'avez donc pas besoin d'une proprecompareTo
méthode.Je recommande toujours d'utiliser Apache commons car ce sera probablement mieux que celui que vous pouvez écrire vous-même. De plus, vous pouvez alors faire un «vrai» travail plutôt que réinventer.
La classe qui vous intéresse est le comparateur nul . Il vous permet de rendre les valeurs nulles hautes ou basses. Vous lui donnez également votre propre comparateur à utiliser lorsque les deux valeurs ne sont pas nulles.
Dans votre cas, vous pouvez avoir une variable membre statique qui effectue la comparaison, puis votre
compareTo
méthode y fait simplement référence.Quelque chose comme
}
Même si vous décidez de lancer la vôtre, gardez cette classe à l'esprit car elle est très utile lors de l'ordre des listes qui contiennent des éléments nuls.
la source
Je sais que cela ne répond peut-être pas directement à votre question, car vous avez dit que les valeurs nulles doivent être prises en charge.
Mais je veux juste noter que la prise en charge des nulls dans compareTo n'est pas conforme au contrat compareTo décrit dans les javadocs officiels de Comparable :
Donc, je lancerais NullPointerException explicitement ou je le laisserais simplement être lancé la première fois lorsqu'un argument nul est déréférencé.
la source
Vous pouvez extraire la méthode:
}
la source
Vous pouvez concevoir votre classe pour qu'elle soit immuable (Effective Java 2nd Ed. A une grande section à ce sujet, Point 15: Minimiser la mutabilité) et vous assurer lors de la construction qu'aucune valeur nulle n'est possible (et utiliser le modèle d'objet nul si nécessaire). Ensuite, vous pouvez ignorer toutes ces vérifications et supposer en toute sécurité que les valeurs ne sont pas nulles.
la source
Je cherchais quelque chose de similaire et cela me semblait un peu compliqué, alors je l'ai fait. Je pense que c'est un peu plus facile à comprendre. Vous pouvez l'utiliser comme comparateur ou comme doublure unique. Pour cette question, vous changeriez pour compareToIgnoreCase (). En l'état, les valeurs nulles flottent. Vous pouvez inverser le 1, -1 si vous voulez qu'ils coulent.
.
la source
nous pouvons utiliser java 8 pour faire une comparaison amicale entre les objets. supposé que j'ai une classe Boy avec 2 champs: nom de chaîne et âge entier et je veux d'abord comparer les noms, puis les âges si les deux sont égaux.
et le résultat:
la source
Au cas où quelqu'un utiliserait Spring, il existe une classe org.springframework.util.comparator.NullSafeComparator qui le fait également pour vous. Décorez simplement votre propre comparable avec comme ça
new NullSafeComparator<YourObject>(new YourComparable(), true)
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/util/comparator/NullSafeComparator.html
la source
Pour le cas spécifique où vous savez que les données n'auront pas de valeurs nulles (toujours une bonne idée pour les chaînes) et que les données sont vraiment volumineuses, vous faites toujours trois comparaisons avant de comparer les valeurs, si vous savez avec certitude que c'est votre cas , vous pouvez optimiser un peu. YMMV en tant que code lisible l'emporte sur l'optimisation mineure:
la source
la sortie est
la source
L'un des moyens simples d' utiliser NullSafe Comparator est d'utiliser l'implémentation Spring de celui-ci, ci-dessous est l'un des exemples simples à consulter:
la source
Un autre exemple d'Apache ObjectUtils. Capable de trier d'autres types d'objets.
la source
C'est mon implémentation que j'utilise pour trier mon ArrayList. les classes nulles sont triées en dernier.
pour mon cas, EntityPhone étend EntityAbstract et mon conteneur est List <EntityAbstract>.
la méthode "compareIfNull ()" est utilisée pour le tri sûr nul. Les autres méthodes sont pour l'exhaustivité, montrant comment compareIfNull peut être utilisé.
la source
Si vous voulez un simple Hack:
si vous voulez mettre des valeurs nulles à la fin de la liste, changez simplement cela dans le metod ci-dessus
la source