J'ai trouvé que Java.lang.Integer
l'implémentation de la méthode compareTo
se présente comme suit:
public int compareTo(Integer anotherInteger) {
int thisVal = this.value;
int anotherVal = anotherInteger.value;
return (thisVal<anotherVal ? -1 : (thisVal==anotherVal ? 0 : 1));
}
La question est de savoir pourquoi utiliser la comparaison au lieu de la soustraction:
return thisVal - anotherVal;
Cela est dû à un débordement d'entier. Lorsque thisVal
est très grand et que anotherVal
est négatif, soustraire ce dernier du premier donne un résultat plus grand que thisVal
qui peut déborder vers la plage négative.
La soustraction "astuce" pour comparer deux valeurs numériques est cassée !!!
int a = -2000000000;
int b = 2000000000;
System.out.println(a - b);
// prints "294967296"
Ici, a < b
, encore a - b
est positif.
N'UTILISEZ PAS cet idiome. Ça ne marche pas.
De plus, même si ça marche, ça [~ # ~] pas [~ # ~] apporter une amélioration significative des performances, et peut en fait lisibilité des coûts.
Ce puzzle a plusieurs leçons. Le plus spécifique est: N'utilisez pas de comparateur basé sur la soustraction, sauf si vous êtes sûr que la différence entre les valeurs ne sera jamais supérieure à
Integer.MAX_VALUE
. Plus généralement, méfiez-vous du débordement deint
. Une autre leçon est que vous devez éviter le code "intelligent". Efforcez-vous d'écrire un code clair et correct et ne l'optimisez pas sauf si cela s'avère nécessaire.
Pour parler simplement, le type int
n'est pas assez grand pour stocker la différence entre deux valeurs arbitraires int
. Par exemple, la différence entre 1,5 milliard et -1,5 milliard est de 3,0 milliards, mais int
ne peut pas contenir des valeurs supérieures à 2,1 milliards.
C'est peut-être pour éviter les débordements/sous-débordements.
En plus du problème de débordement, vous devriez noter que la version avec soustraction ne donne pas les mêmes résultats.
Si vous savez qu'il n'y aura pas de débordement, vous pouvez utiliser quelque chose comme ceci:
public int compareTo(Integer anotherInteger) {
return sign(this.value - anotherInteger.valuel);
}