Je lisais le ArrayList
code source de Java et j'ai remarqué des comparaisons dans les instructions if.
Dans Java 7, la méthode grow(int)
utilise
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
En Java 6, grow
n'existait pas. La méthode ensureCapacity(int)
utilise cependant
if (newCapacity < minCapacity)
newCapacity = minCapacity;
Quelle était la raison du changement? Était-ce un problème de performance ou juste un style?
Je pourrais imaginer que comparer avec zéro est plus rapide, mais effectuer une soustraction complète juste pour vérifier s'il est négatif me semble un peu exagéré. Toujours en termes de bytecode, cela impliquerait deux instructions ( ISUB
et IF_ICMPGE
) au lieu d'une ( IFGE
).
java
if-statement
arraylist
dejvuth
la source
la source
if (newCapacity - minCapacity < 0)
mieux qu'enif (newCapacity < minCapacity)
termes de prévention des débordements?Réponses:
a < b
eta - b < 0
peut signifier deux choses différentes. Considérez le code suivant:Lors de l'exécution, cela ne fera qu'imprimer
a - b < 0
. Ce qui se passe, c'esta < b
clairement faux, maisa - b
déborde et devient-1
, ce qui est négatif.Maintenant, cela dit, considérez que le tableau a une longueur qui est vraiment proche de
Integer.MAX_VALUE
. Le codeArrayList
va comme ceci:oldCapacity
est vraiment proche deInteger.MAX_VALUE
sinewCapacity
( ce qui estoldCapacity + 0.5 * oldCapacity
) risque de déborder et de devenirInteger.MIN_VALUE
(c. -à- négatif). Ensuite, la soustraction desminCapacity
sous-flux revient en un nombre positif.Cette vérification garantit que le
if
n'est pas exécuté. Si le code était écrit en tant queif (newCapacity < minCapacity)
, ce seraittrue
dans ce cas (car ilnewCapacity
est négatif) donc lenewCapacity
serait forcé àminCapacity
indépendamment deoldCapacity
.Ce cas de débordement est géré par le prochain if. Quand
newCapacity
a débordé, ce seratrue
:MAX_ARRAY_SIZE
est défini commeInteger.MAX_VALUE - 8
etInteger.MIN_VALUE - (Integer.MAX_VALUE - 8) > 0
esttrue
. LenewCapacity
est donc correctement géré: lahugeCapacity
méthode retourneMAX_ARRAY_SIZE
ouInteger.MAX_VALUE
.NB: c'est ce que dit le
// overflow-conscious code
commentaire de cette méthode.la source
a - b
et en vérifiant si le bit supérieur est a1
. Comment gèrent-ils le débordement?J'ai trouvé cette explication :
En Java 6, si vous utilisez l'API en tant que:
Et les
newCount
débordements (cela devient négatif),if (minCapacity > oldCapacity)
retourneront faux et vous pouvez supposer à tort que le aArrayList
été augmenté delen
.la source
ensureCapacity
; s'ilminCapacity
est négatif, vous n'y arrivez jamais - il est tout aussi ignoré silencieusement que l'implémentation compliquée prétend l'empêcher. Donc "nous ne pouvons pas faire cela" pour la compatibilité des API publiques est un argument étrange comme ils l'ont déjà fait. Les seuls appelants s'appuyant sur ce comportement sont les internes.minCapacity
est très négatif (c'est-à-dire résultant d'unint
débordement lors de l'ajout de la taille actuelle de la liste de tableaux au nombre d'éléments que vous souhaitez ajouter),minCapacity - elementData.length
pour déborder à nouveau et devenir positif. Voilà comment je le comprends.if (minCapacity > minExpand)
, ce que je ne comprends pas.addAll
méthodes sont le seul cas où elles sont pertinentes car la somme de la taille actuelle et du nombre de nouveaux éléments peut déborder. Néanmoins, ce sont des appels internes et l'argument «nous ne pouvons pas le changer parce queensureCapacity
c'est une API publique» est un argument étrange alors qu'en fait,ensureCapacity
il ignore les valeurs négatives. L'API Java 8 n'a pas changé ce comportement, elle ne fait qu'ignorer les capacités inférieures à la capacité par défaut lorsque leArrayList
est dans son état initial (c'est-à-dire initialisé avec la capacité par défaut et toujours vide).newcount = count + len
est correct en ce qui concerne l'utilisation interne, cependant, il ne s'applique pas à lapublic
méthodeensureCapacity()
...En regardant le code:
Si
oldCapacity
est assez grand, cela débordera etnewCapacity
sera un nombre négatif. Une comparaison comme celle-newCapacity < oldCapacity
ci n'évaluera pas correctementtrue
etArrayList
ne parviendra pas à croître.Au lieu de cela, le code tel qu'il est écrit (
newCapacity - minCapacity < 0
retourne false) permettra à la valeur négative denewCapacity
d'être évaluée plus avant dans la ligne suivante, ce qui entraînera un recalculnewCapacity
en appelanthugeCapacity
(newCapacity = hugeCapacity(minCapacity);
) pour permettre laArrayList
croissance deMAX_ARRAY_SIZE
.C'est ce que le
// overflow-conscious code
commentaire essaie de communiquer, mais de manière plutôt oblique.Donc, en fin de compte, la nouvelle comparaison protège contre l'attribution d'un
ArrayList
plus grand que le prédéfiniMAX_ARRAY_SIZE
tout en lui permettant de croître jusqu'à cette limite si nécessaire.la source
Les deux formes se comportent exactement de la même façon, sauf si l'expression
a - b
déborde, auquel cas elles sont opposées. Sia
est un grand négatif, etb
est un grand positif, alors(a < b)
c'est clairement vrai, maisa - b
débordera pour devenir positif, donc(a - b < 0)
c'est faux.Si vous connaissez le code assembleur x86, considérez qu'il
(a < b)
est implémenté par ajge
, qui se ramifie autour du corps de l'instruction if lorsque SF = OF. D'autre part,(a - b < 0)
agira comme unjns
, qui se ramifie lorsque SF = 0. Par conséquent, ceux-ci se comportent différemment précisément lorsque OF = 1.la source