Estaba leyendo el ArrayList
código fuente de Java y noté algunas comparaciones en sentencias if.
En Java 7, el método grow(int)
usa
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
En Java 6, grow
no existía. Sin ensureCapacity(int)
embargo, el método utiliza
if (newCapacity < minCapacity)
newCapacity = minCapacity;
¿Cuál fue la razón detrás del cambio? ¿Fue un problema de rendimiento o simplemente un estilo?
Me imagino que comparar con cero es más rápido, pero realizar una resta completa solo para verificar si es negativo me parece un poco exagerado. También en términos de bytecode, esto implicaría dos instrucciones ( ISUB
y IF_ICMPGE
) en lugar de una ( IFGE
).
java
if-statement
arraylist
dejvuth
fuente
fuente
if (newCapacity - minCapacity < 0)
mejor queif (newCapacity < minCapacity)
en términos de prevención de desbordamiento?Respuestas:
a < b
ya - b < 0
puede significar dos cosas diferentes. Considere el siguiente código:Cuando se ejecuta, esto solo se imprimirá
a - b < 0
. Lo que sucede es quea < b
es claramente falso, pero sea - b
desborda y se vuelve-1
negativo.Ahora, dicho esto, considere que la matriz tiene una longitud muy cercana
Integer.MAX_VALUE
. El código enArrayList
va así:oldCapacity
está realmente cerca deInteger.MAX_VALUE
lonewCapacity
que (que esoldCapacity + 0.5 * oldCapacity
) podría desbordarse y volverseInteger.MIN_VALUE
(es decir, negativo). Luego, restando losminCapacity
subflujos nuevamente en un número positivo.Esta verificación asegura que
if
no se ejecute. Si el código se escribiera comoif (newCapacity < minCapacity)
, seríatrue
en este caso (ya quenewCapacity
es negativo), por lo quenewCapacity
se vería obligado a hacerlominCapacity
independientemente deoldCapacity
.Este caso de desbordamiento es manejado por el siguiente if. Cuando se
newCapacity
ha desbordado, esto serátrue
:MAX_ARRAY_SIZE
se define comoInteger.MAX_VALUE - 8
yInteger.MIN_VALUE - (Integer.MAX_VALUE - 8) > 0
estrue
. PornewCapacity
lo tanto, se maneja correctamente: elhugeCapacity
método devuelveMAX_ARRAY_SIZE
oInteger.MAX_VALUE
.NB: esto es lo que dice el
// overflow-conscious code
comentario en este método.fuente
a - b
y verificando si el bit superior es a1
. ¿Cómo manejan el desbordamiento?Encontré esta explicación :
En Java 6, si usa la API como:
Y los
newCount
desbordamientos (esto se vuelve negativo),if (minCapacity > oldCapacity)
devolverán falso y puede suponer erróneamente queArrayList
se incrementó enlen
.fuente
ensureCapacity
; siminCapacity
es negativo, nunca se llega a ese punto, se ignora tan silenciosamente como la complicada implementación pretende evitar. Entonces, "no podemos hacer esto" para la compatibilidad de API pública es un argumento extraño como ya lo hicieron. Las únicas personas que llaman confiando en este comportamiento son las internas.minCapacity
es muy negativo (es decir, como resultado delint
desbordamiento al agregar el tamaño actual de ArrayList a la cantidad de elementos que desea agregar),minCapacity - elementData.length
para desbordarse nuevamente y volverse positivo. Así lo entiendo.if (minCapacity > minExpand)
, lo cual no entiendo.addAll
métodos son el único caso en el que es relevante ya que la suma del tamaño actual y el número de elementos nuevos pueden desbordarse. Sin embargo, estas son llamadas internas y el argumento "no podemos cambiarlo porqueensureCapacity
es una API pública" es un argumento extraño cuando, de hecho,ensureCapacity
ignora los valores negativos. La API de Java 8 no cambió ese comportamiento, todo lo que hace es ignorar las capacidades por debajo de la capacidad predeterminada cuandoArrayList
está en su estado inicial (es decir, inicializado con capacidad predeterminada y aún vacío).newcount = count + len
es correcto cuando se trata del uso interno, sin embargo, no se aplica alpublic
métodoensureCapacity()
...Mirando el código:
Si
oldCapacity
es bastante grande, se desbordará ynewCapacity
será un número negativo. Una comparación comonewCapacity < oldCapacity
se evaluará incorrectamentetrue
yArrayList
no crecerá.En cambio, el código tal como está escrito (
newCapacity - minCapacity < 0
devuelve falso) permitirá que el valor negativo denewCapacity
se evalúe más en la siguiente línea, lo que resulta en recalcularnewCapacity
invocandohugeCapacity
(newCapacity = hugeCapacity(minCapacity);
) para permitirArrayList
que crezcaMAX_ARRAY_SIZE
.Esto es lo que el
// overflow-conscious code
comentario intenta comunicar, aunque de manera bastante oblicua.Por lo tanto, en resumen, la nueva comparación protege contra la asignación de un valor
ArrayList
mayor que el predefinido, alMAX_ARRAY_SIZE
tiempo que le permite crecer hasta ese límite si es necesario.fuente
Las dos formas se comportan exactamente igual a menos que la expresión se
a - b
desborde, en cuyo caso son opuestas. Sia
es un gran negativo, yb
es un gran positivo, entonces(a < b)
es claramente cierto, peroa - b
se desbordará para volverse positivo, por lo que(a - b < 0)
es falso.Si está familiarizado con el código de ensamblaje x86, considere que
(a < b)
lo implementa ajge
, que se ramifica alrededor del cuerpo de la instrucción if cuando SF = OF. Por otro lado,(a - b < 0)
actuará como ajns
, que se ramifica cuando SF = 0. Por lo tanto, estos se comportan de manera diferente precisamente cuando OF = 1.fuente