¿Por qué la API de Java usa int en lugar de short o byte?

137

¿Por qué utiliza la API de Java int, cuando shorto incluso bytesería suficiente?

Ejemplo: El DAY_OF_WEEKcampo en los Calendarusos de la clase int.

Si la diferencia es demasiado mínima, ¿por qué existen esos tipos de datos ( short, int)?

Willi Mentzel
fuente

Respuestas:

166

Algunas de las razones ya se han señalado. Por ejemplo, el hecho de que "... (Casi) Todas las operaciones en byte, corto promoverán estas primitivas a int" . Sin embargo, la siguiente pregunta obvia sería: ¿POR QUÉ se promueven estos tipos int?

Entonces, para ir un nivel más profundo: la respuesta simplemente puede estar relacionada con el Conjunto de instrucciones de la máquina virtual Java. Como se resume en la Tabla en la Especificación de máquina virtual Java , todas las operaciones aritméticas integrales, como sumar, dividir y otras, solo están disponibles para el tipo inty el tipo long, y no para los tipos más pequeños.

(Un aparte: los tipos más pequeños ( bytey short) básicamente están destinados solo a matrices . Una matriz como new byte[1000]tomará 1000 bytes, y una matriz como new int[1000]tomará 4000 bytes)

Ahora, por supuesto, uno podría decir que "... la siguiente pregunta obvia sería: ¿POR QUÉ estas instrucciones solo se ofrecen para int(y long)?" .

Una razón se menciona en la especificación JVM mencionada anteriormente:

Si cada instrucción escrita admitiera todos los tipos de datos de tiempo de ejecución de la máquina virtual Java, habría más instrucciones de las que podrían representarse en un byte

Además, la máquina virtual Java puede considerarse como una abstracción de un procesador real. Y la introducción de una unidad lógica aritmética dedicada para tipos más pequeños no valdría la pena: necesitaría transistores adicionales, pero aún así solo podría ejecutar una adición en un ciclo de reloj. La arquitectura dominante cuando se diseñó la JVM era de 32 bits, justo para un 32 bits int. (Las operaciones que involucran un longvalor de 64 bits se implementan como un caso especial).

(Nota: el último párrafo está un poco simplificado, considerando la posible vectorización, etc., pero debe dar la idea básica sin profundizar demasiado en los temas de diseño del procesador)


EDITAR: Una breve adición, centrada en el ejemplo de la pregunta, pero en un sentido más general: también se podría preguntar si no sería beneficioso almacenar campos utilizando los tipos más pequeños. Por ejemplo, uno podría pensar que la memoria se podría guardar almacenando Calendar.DAY_OF_WEEKcomo a byte. Pero aquí, el formato de archivo de clase Java entra en juego: todos los campos en un archivo de clase ocupan al menos un "espacio", que tiene el tamaño de uno int(32 bits). (Los campos "anchos" doubley longocupan dos espacios). Por lo tanto, declarar explícitamente un campo como shorto byteno guardaría ninguna memoria tampoco.

Marco13
fuente
Supongo que la lógica de por qué los operandos son promovidos a int también está relacionada con la lógica utilizada en C y C ++
Shafik Yaghmour
@ Marco13 "Así que declarar explícitamente un campo como corto o byte tampoco ahorraría memoria". ¿es eso cierto? No creo que sea correcto.
ACV
@ACV Hablando estrictamente, una implementación podría optar por almacenar una forma más compacta, pero el formato que está expuesto "virtualmente" (es decir, por la máquina virtual) tratará los valores con al menos el tamaño de int. Si tiene una referencia a otra implementación, actualizaría la respuesta e insertaría el enlace en consecuencia.
Marco13
40

(Casi) Todas las operaciones activadas byte, las shortpromocionarán a int, por ejemplo, no puede escribir:

short x = 1;
short y = 2;

short z = x + y; //error

La aritmética es más fácil y directa cuando se usa int, no es necesario lanzar.

En términos de espacio, tiene una muy poca diferencia. bytey shortcomplicaría las cosas, no creo que esta micro optimización valga la pena ya que estamos hablando de una cantidad fija de variables.

bytees relevante y útil cuando programa para dispositivos integrados o cuando trata con archivos / redes. Además, estas primitivas son limitadas, ¿y si los cálculos pudieran exceder sus límites en el futuro? Trate de pensar en una extensión para la Calendarclase que pueda desarrollar números más grandes.

También tenga en cuenta que en los procesadores de 64 bits, los locales se guardarán en los registros y no utilizar ningún recurso, por lo que usar int, shorty otras primitivas no hará ninguna diferencia en absoluto. Además, muchas implementaciones de Java alinean variables * (y objetos).


* byte y shortocupan el mismo espacio como intsi fueran variables locales , variables de clase o incluso variables de instancia . ¿Por qué? Porque en (la mayoría) de los sistemas informáticos, las direcciones de las variables están alineadas , por lo que, por ejemplo, si usa un solo byte, en realidad terminará con dos bytes, uno para la variable en sí y otro para el relleno.

Por otro lado, en las matrices, bytetome 1 byte, shorttome 2 bytes y inttome cuatro bytes, porque en las matrices solo el inicio y quizás el final deben estar alineados. Esto hará una diferencia en caso de que quiera usar, por ejemplo System.arraycopy(), entonces realmente notará una diferencia de rendimiento.

Maroun
fuente
1
Dato curioso: si usa modificadores finales para ambos valores, funcionará. :)
alexander
7

Porque las operaciones aritméticas son más fáciles cuando se usan números enteros en comparación con los cortos. Suponga que las constantes realmente fueron modeladas por shortvalores. Entonces tendrías que usar la API de esta manera:

short month = Calendar.JUNE;
month = month + (short) 1; // is july

Observe el casting explícito. Los valores cortos se promueven implícitamente a intvalores cuando se usan en operaciones aritméticas. (En la pila de operandos, los cortos incluso se expresan como int.) Esto sería bastante engorroso de usar, por lo intque a menudo se prefieren los valores para las constantes.

En comparación con eso, la ganancia en eficiencia de almacenamiento es mínima porque solo existe un número fijo de tales constantes. Estamos hablando de 40 constantes. Cambiar su almacenamiento de inta shortte salvaría 40 * 16 bit = 80 byte. Vea esta respuesta para mayor referencia.

Rafael Winterhalter
fuente
5

Si utilizó la filosofía en la que las constantes integrales se almacenan en el tipo más pequeño que caben, entonces Java tendría un problema grave: cada vez que los programadores escriben código utilizando constantes integrales, deben prestar especial atención a su código para verificar si el tipo de las constantes importan, y si es así, busque el tipo en la documentación y / o haga las conversiones de tipo necesarias.

Entonces, ahora que hemos esbozado un problema grave, ¿qué beneficios podría esperar lograr con esa filosofía? No me sorprendería si el único efecto observable en el tiempo de ejecución de ese cambio fuera el tipo que obtienes cuando miras la constante hacia arriba a través de la reflexión. (y, por supuesto, los errores introducidos por programadores perezosos / involuntarios que no representan correctamente los tipos de las constantes)

Sopesar los pros y los contras es muy fácil: es una mala filosofía.


fuente
4

La complejidad del diseño de una máquina virtual depende de la cantidad de operaciones que puede realizar. Es más fácil tener cuatro implementaciones de una instrucción como "multiplicar", una para números enteros de 32 bits, números enteros de 64 bits, punto flotante de 32 bits y punto flotante de 64 bits, que tener, además a lo anterior, versiones para los tipos numéricos más pequeños también. Una pregunta de diseño más interesante es por qué debería haber cuatro tipos, en lugar de menos (realizar todos los cálculos de enteros con enteros de 64 bits y / o hacer todos los cálculos de punto flotante con valores de punto flotante de 64 bits). La razón para usar números enteros de 32 bits es que se esperaba que Java se ejecutara en muchas plataformas en las que los tipos de 32 bits podrían actuar tan rápido como los tipos de 16 u 8 bits, pero las operaciones en tipos de 64 bits serían notablemente más lento.solo tiene tipos de 32 bits.

En cuanto a realizar cálculos de punto flotante en valores de 32 bits, las ventajas son un poco menos claras. Hay algunas plataformas donde un cálculo comofloat a=b+c+d;podría realizarse más rápidamente al convertir todos los operandos a un tipo de mayor precisión, agregarlos y luego convertir el resultado a un número de coma flotante de 32 bits para el almacenamiento. Hay otras plataformas donde sería más eficiente realizar todos los cálculos utilizando valores de coma flotante de 32 bits. Los creadores de Java decidieron que se debería exigir a todas las plataformas que hicieran las cosas de la misma manera, y que deberían favorecer las plataformas de hardware para las cuales los cálculos de punto flotante de 32 bits son más rápidos que los más largos, a pesar de que esta PC degradó severamente la velocidad y precisión de las matemáticas de punto flotante en una PC típica, así como en muchas máquinas sin unidades de punto flotante. Tenga en cuenta, por cierto, que dependiendo de los valores de b, c y d, usar cálculos intermedios de mayor precisión al calcular expresiones como las mencionadas anteriormentefloat a=b+c+d;a veces arrojará resultados que son significativamente más precisos que los que se obtendrían de todos los operandos intermedios que se calcularon con floatprecisión, pero a veces arrojará un valor que es un poco menos preciso. En cualquier caso, Sun decidió que todo debería hacerse de la misma manera, y optaron por usar floatvalores de precisión mínima .

Tenga en cuenta que las principales ventajas de los tipos de datos más pequeños se hacen evidentes cuando un gran número de ellos se almacenan juntos en una matriz; incluso si no hubiera ninguna ventaja de tener variables individuales de tipos más pequeños que 64 bits, vale la pena tener matrices que puedan almacenar valores más pequeños de manera más compacta; tener una variable local sea en bytelugar de un longahorro de siete bytes; tener una matriz de 1,000,000 de números contiene cada número como un en bytelugar de unlongagita 7,000,000 bytes. Dado que cada tipo de matriz solo necesita admitir algunas operaciones (especialmente leer un elemento, almacenar un elemento, copiar un rango de elementos dentro de una matriz o copiar un rango de elementos de una matriz a otra), la complejidad añadida de tener más Los tipos de matriz no son tan graves como la complejidad de tener más tipos de valores numéricos discretos directamente utilizables.

Super gato
fuente
2

En realidad, habría una pequeña ventaja. Si tienes un

class MyTimeAndDayOfWeek {
    byte dayOfWeek;
    byte hour;
    byte minute;
    byte second;
}

luego, en una JVM típica, necesita tanto espacio como una clase que contenga una sola int. El consumo de memoria se redondea al siguiente múltiplo de 8 o 16 bytes (IIRC, eso es configurable), por lo que los casos en los que hay un ahorro real son bastante raros.

Esta clase sería un poco más fácil de usar si los Calendarmétodos correspondientes devolvieran a byte. Pero no existen tales Calendarmétodos, solo get(int)que deben devolver y intdebido a otros campos. Cada operación en tipos más pequeños promueve int, por lo que necesita una gran cantidad de fundición.

Lo más probable es que te rindas y cambies a una into escribas setters como

void setDayOfWeek(int dayOfWeek) {
    this.dayOfWeek = checkedCastToByte(dayOfWeek);
}

Entonces el tipo de DAY_OF_WEEKno importa, de todos modos.

maaartinus
fuente