services issue create apis java if-statement arraylist

java - issue - Diferencia entre if(a-b<0) y if(a<b)



jira rest api definition (4)

Estaba leyendo el código fuente ArrayList 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, el grow no existía. Sin ensureCapacity(int) método ensureCapacity(int) usa

if (newCapacity < minCapacity) newCapacity = minCapacity;

¿Cuál fue la razón detrás del cambio? ¿Fue un problema de rendimiento o simplemente un estilo?

Podría imaginar 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 e IF_ICMPGE ) en lugar de una ( IFGE ).


Encontré esta explicación :

El martes 9 de marzo de 2010 a las 03:02, Kevin L. Stern escribió:

Hice una búsqueda rápida y parece que Java está basado en el complemento de dos. No obstante, permítame señalar que, en general, este tipo de código me preocupa, ya que espero que en algún momento alguien venga y haga exactamente lo que Dmytro sugirió; es decir, alguien cambiará:

if (a - b > 0)

a

if (a > b)

y toda la nave se hundirá. Personalmente, me gusta evitar las obscuridades, como hacer que el desbordamiento de enteros sea una base esencial para mi algoritmo a menos que haya una buena razón para hacerlo. En general, preferiría evitar el desbordamiento por completo y hacer que el escenario de desbordamiento sea más explícito:

if (oldCapacity > RESIZE_OVERFLOW_THRESHOLD) { // Do something } else { // Do something else }

Es un buen punto.

En ArrayList no podemos hacer esto (o al menos no de manera compatible), porque ensureCapacity es una API pública y efectivamente ya acepta números negativos como solicitudes de una capacidad positiva que no puede satisfacerse.

La API actual se usa así:

int newcount = count + len; ensureCapacity(newcount);

Si desea evitar el desbordamiento, deberá cambiar a algo menos natural como

ensureCapacity(count, len); int newcount = count + len;

De todos modos, mantengo el código consciente del desbordamiento, pero agrego más comentarios de advertencia y crea una gran variedad de "esquemas" para que el código de ArrayList ahora se vea así:

/** * Increases the capacity of this <tt>ArrayList</tt> instance, if * necessary, to ensure that it can hold at least the number of elements * specified by the minimum capacity argument. * * @param minCapacity the desired minimum capacity */ public void ensureCapacity(int minCapacity) { modCount++; // Overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); } /** * The maximum size of array to allocate. * Some VMs reserve some header words in an array. * Attempts to allocate larger arrays may result in * OutOfMemoryError: Requested array size exceeds VM limit */ private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; /** * Increases the capacity to ensure that it can hold at least the * number of elements specified by the minimum capacity argument. * * @param minCapacity the desired minimum capacity */ private void grow(int minCapacity) { // Overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); } private int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }

Webrev regenerado.

Martín

En Java 6, si usa la API como:

int newcount = count + len; ensureCapacity(newcount);

Y se desborda newCount (esto se vuelve negativo), if (minCapacity > oldCapacity) devolverá falso y puede suponer erróneamente que len aumentó la ArrayList .


Las dos formas se comportan exactamente igual a menos que la expresión a - b desborde, en cuyo caso son opuestas. Si a es un gran negativo, b es un gran positivo, entonces (a < b) es claramente cierto, pero a - b se desbordará para volverse positivo, entonces (a - b < 0) es falso.

Si está familiarizado con el código de ensamblaje x86, considere que (a < b) se implementa mediante un jge , que se ramifica alrededor del cuerpo de la instrucción if cuando SF = OF. Por otro lado, (a - b < 0) actuará como un jns , que se ramifica cuando SF = 0. Por lo tanto, estos se comportan de manera diferente precisamente cuando OF = 1.


Mirando el código:

int newCapacity = oldCapacity + (oldCapacity >> 1);

Si oldCapacity es bastante grande, esto se desbordará y newCapacity será un número negativo. Una comparación como newCapacity < oldCapacity evaluará incorrectamente true y ArrayList no crecerá.

En cambio, el código como está escrito ( newCapacity - minCapacity < 0 devuelve falso) permitirá que el valor negativo de newCapacity se evalúe más en la siguiente línea, lo que resulta en recalcular newCapacity invocando hugeCapacity ( newCapacity = hugeCapacity(minCapacity); ) para permitir ArrayList para crecer hasta MAX_ARRAY_SIZE .

Esto es lo que el // overflow-conscious code comentario de // overflow-conscious code intenta comunicar, aunque de manera bastante oblicua.

Entonces, en MAX_ARRAY_SIZE , la nueva comparación protege contra la asignación de una ArrayList más grande que la MAX_ARRAY_SIZE predefinida, MAX_ARRAY_SIZE tiempo que le permite crecer hasta ese límite si es necesario.


a < b a - b < 0 puede significar dos cosas diferentes. Considere el siguiente código:

int a = Integer.MAX_VALUE; int b = Integer.MIN_VALUE; if (a < b) { System.out.println("a < b"); } if (a - b < 0) { System.out.println("a - b < 0"); }

Cuando se ejecuta, esto solo imprimirá a - b < 0 . Lo que sucede es que a < b es claramente falso, pero a - b desborda y se convierte en -1 , lo cual es negativo.

Ahora, dicho esto, considere que la matriz tiene una longitud muy cercana a Integer.MAX_VALUE . El código en ArrayList es así:

int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity);

oldCapacity está realmente cerca de Integer.MAX_VALUE por lo que newCapacity (que es oldCapacity + 0.5 * oldCapacity ) puede desbordarse y convertirse en Integer.MIN_VALUE (es decir, negativo). Luego, restando minCapacity desborda nuevamente en un número positivo.

Esta comprobación asegura que el if no se ejecute. Si el código se escribiera como if (newCapacity < minCapacity) , sería true en este caso (dado que newCapacity es negativo), por lo que newCapacity se vería obligado a minCapacity independientemente de la oldCapacity .

Este caso de desbordamiento es manejado por el siguiente if. Cuando newCapacity ha desbordado, esto será true : MAX_ARRAY_SIZE se define como Integer.MAX_VALUE - 8 e Integer.MIN_VALUE - (Integer.MAX_VALUE - 8) > 0 es true . Por lo tanto, newCapacity se maneja correctamente: el método MAX_ARRAY_SIZE devuelve MAX_ARRAY_SIZE o Integer.MAX_VALUE .

NB: esto es lo que dice el // overflow-conscious code comentario de // overflow-conscious code en este método.