comparateur avec des valeurs nulles

Nous avons du code qui sortinge une liste d’adresses en fonction de la distance entre leurs coordonnées. Cela se fait via collections.sort avec un comparateur personnalisé.

Cependant, de temps en temps, une adresse sans coordonnées se trouve dans la liste provoquant une exception NullPointerException. Mon idée initiale pour résoudre ce problème était de faire en sorte que le comparateur renvoie 0 comme dististance pour les adresses où au moins une des coordonnées est nulle. Je crains que cela puisse conduire à la corruption de la commande des éléments «valides» de la liste.

il renvoie donc une valeur «0» pour les données nulles dans un comparateur ok, ou existe-t-il un moyen plus propre de résoudre ce problème.

Manipulez-le comme null signifie infiniment loin. Ainsi:

  • comp(1234, null) == -1
  • comp(null, null) == 0
  • comp(null, 1234) == 1

Avec cela, vous obtenez une commande cohérente.

Pour approfondir la réponse de Willi Schönborn, je suis venu ici pour dire que google-collections est exactement ce que vous recherchez ici.

Dans le cas général, vous pouvez simplement écrire votre propre Comparator pour ignorer les valeurs NULL (supposez que NULL est nul, afin qu’il puisse se concentrer sur la logique importante), puis utilisez Ordering pour gérer les valeurs NULL:

 Collections.sort(addresses, Ordering.from(new AddressComparator()).nullsLast()); 

Dans votre cas, cependant, ce sont les données dans l’adresse (les coordonnées) qui sont utilisées pour sortinger, n’est-ce pas? google-collections est encore plus utile dans ce cas. Donc, vous pourriez avoir quelque chose de plus comme:

 // Seems verbose at first glance, but you'll probably find yourself reusing // this a lot and it will pay off quickly. private static final Function ADDRESS_TO_COORDINATES = new Function() { public Coordinates apply(Address in) { return in.getCoordinates(); } }; private static final Comparator COORDINATE_SORTER = .... // existing 

alors quand vous voulez sortinger:

 Collections.sort(addresses, Ordering.from(COORDINATE_SORTER) .nullsLast() .onResultOf(ADDRESS_TO_COORDINATES)); 

et c’est là que le pouvoir de Google-Collections commence à porter ses fruits.

Mon sharepoint vue est que tout ce que vous essayez de faire pour “corriger” les coordonnées null fait que passer au travers des fissures. Ce que vous devez vraiment faire, c’est trouver et corriger les bogues qui injectent les coordonnées null parasites.

D’après mon expérience, les infestations de bogues NPE sont souvent causées par les mauvaises habitudes de codage suivantes:

  • validation insuffisante des parameters d’entrée,
  • en utilisant null pour éviter de créer des tableaux ou des collections vides,
  • renvoyer null lorsqu’une exception aurait dû être lancée, ou
  • utiliser null pour représenter “no value” lorsqu’il existe une meilleure solution.

(De meilleures solutions au problème “sans valeur” impliquent généralement la réécriture du code afin que vous n’ayez pas besoin de le représenter et / ou d’utiliser une valeur non nulle à la place, par exemple une chaîne vide, une instance spéciale, une valeur réservée. ne trouvez pas toujours une meilleure solution, mais vous le pouvez souvent.)

Si cela décrit votre application, vous devriez consacrer du temps à résoudre les problèmes de code plutôt que de penser aux manières de masquer les NPE.

Ma solution (peut être utile pour quelqu’un qui regarde ici) est de faire la comparaison normale, avec des valeurs nulles remplacées non par 0, mais la valeur maximale possible (par exemple Integer.MAX_VALUE). Renvoyer 0 n’est pas cohérent si vous avez des valeurs qui sont elles-mêmes 0. Voici un exemple correct:

  public int compare(YourObject lhs, YourObject rhs) { Integer l = Integer.MAX_VALUE; Integer r = Integer.MAX_VALUE; if (lhs != null) { l = lhs.giveMeSomeMeasure(); } if (rhs != null) { r = rhs.giveMeSomeMeasure(); } return l.compareTo(r); } 

Je voulais juste append que vous n’avez pas nécessairement besoin de la valeur maximale pour un nombre entier. Cela dépend de ce que votre méthode giveMeSomeMeasure () peut renvoyer. Si, par exemple, vous comparez les degrés Celsius pour la météo, vous pouvez définir l et r sur -300 ou +300, selon l’endroit où vous voulez définir les objects nuls – en tête ou en queue de la liste.

Vous ne voulez probablement pas retourner 0 car cela implique que les adresses sont équidistantes et vous ne le savez vraiment pas. C’est un problème assez classique lorsque vous essayez de gérer de mauvaises données d’entrée. Je ne pense pas que ce soit la responsabilité du comparateur d’essayer de déterminer dans quelle mesure l’adresse est en termes réels lorsque vous ne connaissez pas la distance. Je supprimerais ces adresses de la liste avant de les sortinger.

Le hack serait de les déplacer au bas de la liste (mais c’est moche!)

Si vous utilisez Java 8, vous disposez de deux nouvelles méthodes statiques dans la classe Comparator, qui sont utiles:

 public static  Comparator nullsFirst(Comparator comparator) public static  Comparator nullsLast(Comparator comparator) 

La comparaison sera nullement sûre et vous pouvez choisir où placer les valeurs NULL dans la séquence sortingée.

L’exemple suivant:

 List monkeyBusiness = Arrays.asList("Chimp", "eat", "sleep", "", null, "banana", "throw banana peel", null, "smile", "run"); Comparator comparator = (a, b) -> a.compareTo(b); monkeyBusiness.stream().sorted(Comparator.nullsFirst(comparator)) .forEach(x -> System.out.print("[" + x + "] ")); 

va imprimer: [null] [null] [] [chimpanzé] [banane] [manger] [exécuter] [dormir] [sourire] [jeter la peau de banane]

Au lieu de regarder cela comme un problème technique avec le comparateur, il est préférable de revoir les exigences: ce que vous essayez vraiment de faire ici, que ferez-vous avec cette liste sortingée?

  • Si vous essayez de les sortinger pour montrer d’abord les solutions les plus pertinentes à un utilisateur, il peut être judicieux de placer les emplacements inconnus en dernier, donc traitez-le comme un infini (en retournant 0 / -1 / 1 nul).
  • Si vous allez utiliser ce résultat pour dessiner un graphique ou effectuer d’autres calculs qui dépendent de leur sorting réel par leur distance, alors les nulls n’auraient probablement pas dû être là (donc supprimez-les en premier ou lancez un calcul). exception si, à ce moment-là, il n’y avait aucune adresse avec un emplacement nul).

Comme vous vous en doutez déjà, retourner toujours 0 quand l’un d’eux est nul n’est pas une bonne idée ici; il peut en effet corrompre le résultat. Mais ce que vous devriez faire à la place dépend de ce dont vous avez besoin, pas de ce que les autres font ou ont généralement besoin. Le comportement de votre programme avec les adresses qui n’ont pas d’emplacement (ce que l’utilisateur va voir) ne devrait pas dépendre de certains détails techniques, comme la «meilleure pratique» pour les comparateurs. (Pour moi, demander quelle est la “meilleure pratique” ici ressemble à demander quelles sont les “meilleures exigences”).

Non, il n’y a pas de moyen plus propre. Peut-être:

  • si les coordonnées des deux objects comparés sont nulles, renvoyez 0
  • si les coordonnées de l’un des objects sont nulles, renvoyer -1 / 1 (selon qu’il s’agit du premier ou du deuxième argument)

Mais plus important encore, essayez de vous débarrasser des coordonnées manquantes ou de les remplir, ou mieux: ne mettez pas d’adresse avec des coordonnées manquantes dans la liste.

En fait, ne pas les mettre dans la liste est le comportement le plus logique. Si vous les mettez dans la liste, le résultat ne sera pas réellement ordonné en fonction de la distance.

Vous pouvez créer une autre liste, contenant les adresses avec des coordonnées manquantes, et indiquer clairement à la personne qui en a besoin (utilisateur final, utilisateur de l’API) que la première liste ne contient que les adresses contenant les données nécessaires. manque d’informations requirejses.

Personnellement, je déteste faire face à des affaires null null partout dans mes comparateurs, donc je cherchais une solution de nettoyage plus propre et j’ai finalement trouvé les collections google. Leur commande est juste géniale. Ils prennent en charge les comparateurs composés, proposent de sortinger les valeurs nulles vers le haut et vers la fin et permettent d’exécuter certaines fonctions avant de comparer. Écrire des comparateurs n’a jamais été aussi facile. Tu devrais essayer.