Eu estava lendo o ArrayList
código fonte do Java e notei algumas comparações nas instruções if.
No Java 7, o método grow(int)
usa
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
No Java 6, grow
não existia. O método, ensureCapacity(int)
no entanto, usa
if (newCapacity < minCapacity)
newCapacity = minCapacity;
Qual foi o motivo por trás da mudança? Foi um problema de desempenho ou apenas um estilo?
Eu poderia imaginar que comparar com zero é mais rápido, mas realizar uma subtração completa apenas para verificar se é negativo parece um pouco exagero para mim. Também em termos de bytecode, isso envolveria duas instruções ( ISUB
e IF_ICMPGE
) em vez de uma ( IFGE
).
java
if-statement
arraylist
dejvuth
fonte
fonte
if (newCapacity - minCapacity < 0)
melhor do queif (newCapacity < minCapacity)
em termos de prevenção de estouro?Respostas:
a < b
ea - b < 0
pode significar duas coisas diferentes. Considere o seguinte código:Quando executado, isso será impresso apenas
a - b < 0
. O que acontece é quea < b
é claramente falso, masa - b
transborda e se torna-1
negativo.Agora, tendo dito isso, considere que a matriz tem um comprimento muito próximo
Integer.MAX_VALUE
. O códigoArrayList
é assim:oldCapacity
está realmente próximo deInteger.MAX_VALUE
quenewCapacity
(que éoldCapacity + 0.5 * oldCapacity
) pode transbordar e se tornarInteger.MIN_VALUE
(ou seja, negativo). SubtraindominCapacity
subfluxos de volta para um número positivo.Essa verificação garante que o
if
não seja executado. Se o código fosse escrito comoif (newCapacity < minCapacity)
, seriatrue
neste caso (uma vez quenewCapacity
é negativo) e, portanto,newCapacity
seria forçado aminCapacity
independentemente dooldCapacity
.Esse caso de estouro é tratado pelo próximo if. Quando
newCapacity
estourou, serátrue
:MAX_ARRAY_SIZE
é definido comoInteger.MAX_VALUE - 8
eInteger.MIN_VALUE - (Integer.MAX_VALUE - 8) > 0
étrue
. AnewCapacity
é, por conseguinte, tratadas com razão:hugeCapacity
método retornaMAX_ARRAY_SIZE
ouInteger.MAX_VALUE
.Nota: é o que o
// overflow-conscious code
comentário deste método está dizendo.fonte
a - b
e verificando se o bit superior é a1
. Como eles lidam com estouro?Eu encontrei esta explicação :
No Java 6, se você usar a API como:
E os
newCount
estouros (isso se torna negativo)if (minCapacity > oldCapacity)
retornará falso e você pode assumir, por engano, que oArrayList
aumento foi delen
.fonte
ensureCapacity
; seminCapacity
for negativo, você nunca chega a esse ponto - é ignorado tão silenciosamente quanto a implementação complicada pretende impedir. Portanto, "não podemos fazer isso" para compatibilidade com API pública é um argumento estranho, como eles já fizeram. Os únicos chamadores que dependem desse comportamento são os internos.minCapacity
for muito negativo (ou seja, resultante doint
estouro ao adicionar o tamanho atual da ArrayList ao número de elementos que você deseja adicionar),minCapacity - elementData.length
transbordar novamente e tornar-se positivo. É assim que eu entendo.if (minCapacity > minExpand)
, o que eu não entendo.addAll
métodos são o único caso em que é relevante, pois a soma do tamanho atual e o número de novos elementos podem exceder. No entanto, essas são chamadas internas e o argumento "não podemos alterá-lo porqueensureCapacity
é uma API pública" é um argumento estranho quando, de fato,ensureCapacity
ignora valores negativos. A API do Java 8 não mudou esse comportamento, tudo o que faz é ignorar capacidades abaixo da capacidade padrão quandoArrayList
ela estiver no estado inicial (isto é, inicializada com a capacidade padrão e ainda vazia).newcount = count + len
é correto quando se trata do uso interno, no entanto, não se aplica aopublic
métodoensureCapacity()
...Olhando para o código:
Se
oldCapacity
for muito grande, isso excederá enewCapacity
será um número negativo. Uma comparação comonewCapacity < oldCapacity
será avaliada incorretamentetrue
e aArrayList
falha no crescimento.Em vez disso, o código gravado (
newCapacity - minCapacity < 0
retorna false) permitirá que o valor negativonewCapacity
seja avaliado ainda mais na próxima linha, resultando em um novo cálculonewCapacity
invocandohugeCapacity
(newCapacity = hugeCapacity(minCapacity);
) para permitir que o valorArrayList
cresçaMAX_ARRAY_SIZE
.É isso que o
// overflow-conscious code
comentário está tentando comunicar, embora de maneira oblíqua.Portanto, a nova comparação protege contra a alocação de um valor
ArrayList
maior que o predefinidoMAX_ARRAY_SIZE
, permitindo que ele cresça até esse limite, se necessário.fonte
As duas formas se comportam exatamente da mesma forma, a menos que a expressão
a - b
transborde, caso em que são opostas. Sea
é um grande negativo eb
é um grande positivo, então(a < b)
é claramente verdade, masa - b
transbordará para se tornar positivo, o que(a - b < 0)
é falso.Se você estiver familiarizado com o código de montagem x86, considere que ele
(a < b)
é implementado por ajge
, que se ramifica no corpo da instrução if quando SF = OF. Por outro lado,(a - b < 0)
agirá como ajns
, que se ramifica quando SF = 0. Portanto, eles se comportam de maneira diferente com precisão quando OF = 1.fonte