Por que a API Java usa int em vez de curto ou byte?

137

Por que a API Java usa int, quando shortou mesmo byteseria suficiente?

Exemplo: O DAY_OF_WEEKcampo na classe Calendarusa int.

Se a diferença é mínima demais, por que esses tipos de dados ( short, int) existem?

Willi Mentzel
fonte

Respostas:

166

Algumas das razões já foram apontadas. Por exemplo, o fato de "... (Quase) Todas as operações no byte, curto promoverão essas primitivas para int" . No entanto, a próxima pergunta óbvia seria: POR QUE esses tipos são promovidos int?

Então, para ir um nível mais além: a resposta pode estar simplesmente relacionada ao conjunto de instruções da Java Virtual Machine. Conforme resumido na Tabela em Java Virtual Machine Specification , todas as operações aritméticas integrais, como adição, divisão e outras, estão disponíveis apenas para o tipo inte o tipo long, e não para os tipos menores.

(Além disso: os tipos menores ( bytee short) destinam-se basicamente apenas a matrizes . Uma matriz como new byte[1000]terá 1000 bytes e uma matriz como new int[1000]4000 bytes)

Agora, é claro, alguém poderia dizer que "... a próxima pergunta óbvia seria: POR QUE essas instruções são oferecidas apenas para int(e long)?" .

Um motivo é mencionado nas especificações da JVM mencionadas acima:

Se cada instrução digitada suportasse todos os tipos de dados de tempo de execução da Java Virtual Machine, haveria mais instruções do que poderiam ser representadas em um byte

Além disso, a Java Virtual Machine pode ser considerada como uma abstração de um processador real. E a introdução da Unidade Lógica Aritmética dedicada para tipos menores não valeria o esforço: precisaria de transistores adicionais, mas ainda assim poderia executar apenas uma adição em um ciclo de clock. A arquitetura dominante quando a JVM foi projetada era de 32 bits, ideal para 32 bits int. (As operações que envolvem um longvalor de 64 bits são implementadas como um caso especial).

(Nota: o último parágrafo é um pouco simplificado, considerando possíveis vetorizações etc., mas deve fornecer a idéia básica sem se aprofundar nos tópicos de design do processador)


EDIT: Um pequeno adendo, focado no exemplo da pergunta, mas em um sentido mais geral: também se poderia perguntar se não seria benéfico armazenar campos usando os tipos menores. Por exemplo, pode-se pensar que a memória pode ser salva armazenando Calendar.DAY_OF_WEEKcomo a byte. Mas aqui o Java Class File Format entra em jogo: Todos os campos em um arquivo de classe ocupam pelo menos um "slot", que tem o tamanho de um int(32 bits). (Os campos "largos" doublee longocupam dois slots). Declarar de forma explícita um campo como shortou bytenão guardar qualquer memória também.

Marco13
fonte
Eu diria que a lógica de porque operandos são promovidos para int também está relacionada com a metodologia utilizada em C e C ++
Shafik Yaghmour
@ Marco13 "Declarar explicitamente um campo como curto ou byte também não salvaria memória." isso é verdade? Eu não acho isso correto.
ACV 27/07
@ACV A rigor, uma implementação pode optar por armazenar uma forma mais compacta, mas o formato exposto "virtualmente" (ou seja, pela máquina virtual) tratará os valores como tendo pelo menos o tamanho de int. Se você tem uma referência a outra implementação, atualizo a resposta e insiro o link adequadamente.
Marco13
40

(Quase) Todas as operações em byte, shortvai promovê-los para int, por exemplo, você não pode escrever:

short x = 1;
short y = 2;

short z = x + y; //error

A aritmética é mais fácil e direta ao usar int, sem necessidade de fundição.

Em termos de espaço, faz uma diferença muito pequena. bytee shortcomplicaria as coisas, não acho que essa micro otimização valha a pena, pois estamos falando de uma quantidade fixa de variáveis.

byteé relevante e útil quando você programa para dispositivos incorporados ou ao lidar com arquivos / redes. Além disso, essas primitivas são limitadas, e se os cálculos puderem exceder seus limites no futuro? Tente pensar em uma extensão para a Calendarclasse que pode evoluir em números maiores.

Observe também que em processadores de 64 bits, os moradores serão guardados nos registos e não vai usar todos os recursos, portanto, usando int, shorte outros primitivos não fará qualquer diferença. Além disso, muitas implementações Java alinham variáveis * (e objetos).


* byte e shortocupam o mesmo espaço como intse fossem variáveis locais , variáveis ​​de classe ou mesmo variáveis ​​de instância . Por quê? Como na maioria dos sistemas de computadores, os endereços das variáveis ​​são alinhados ; por exemplo, se você usar um único byte, você terá dois bytes - um para a variável em si e outro para o preenchimento.

Por outro lado, em matrizes, byteuse 1 byte, short2 e int4 bytes, porque nas matrizes apenas o início e talvez o final dela devem estar alinhados. Isso fará a diferença no caso de você querer usar, por exemplo System.arraycopy(), e notará realmente uma diferença de desempenho.

Maroun
fonte
1
Curiosidade: se você usar modificadores finais para ambos os valores, ele funcionará. :)
alexander
7

Como as operações aritméticas são mais fáceis ao usar números inteiros em comparação com curtos. Suponha que as constantes foram realmente modeladas por shortvalores. Então você teria que usar a API desta maneira:

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

Observe a transmissão explícita. Valores curtos são implicitamente promovidos para intvalores quando usados ​​em operações aritméticas. (Na pilha de operandos, os shorts são expressos como ints.) Isso seria bastante complicado de usar, e é por isso que os intvalores são geralmente preferidos para constantes.

Comparado a isso, o ganho em eficiência de armazenamento é mínimo, porque existe apenas um número fixo dessas constantes. Estamos falando de 40 constantes. Alterar o armazenamento de intpara para shortprotegeria você 40 * 16 bit = 80 byte. Veja esta resposta para referência adicional.

Rafael Winterhalter
fonte
5

Se você usasse a filosofia em que constantes integrais são armazenadas no menor tipo em que se encaixam, o Java teria um problema sério: sempre que os programadores escrevem código usando constantes integrais, eles devem prestar muita atenção ao código para verificar se o tipo de as constantes são importantes e, se assim for, procure o tipo na documentação e / ou faça as conversões de tipo necessárias.

Então, agora que descrevemos um problema sério, que benefícios você poderia esperar obter com essa filosofia? Eu não ficaria surpreso se o único efeito observável em tempo de execução dessa alteração fosse o tipo que você obtém quando procura a constante por meio de reflexão. (e, é claro, quaisquer que sejam os erros introduzidos por programadores preguiçosos / inconscientes que não respondem corretamente aos tipos de constantes)

Pesar os prós e os contras é muito fácil: é uma filosofia ruim.


fonte
4

A complexidade do design de uma máquina virtual é uma função de quantos tipos de operações ela pode executar. É mais fácil ter quatro implementações de uma instrução como "multiplicar" - uma para número inteiro de 32 bits, número inteiro de 64 bits, ponto flutuante de 32 bits e ponto flutuante de 64 bits - do que ter, além disso acima, versões para os tipos numéricos menores também. Uma questão de design mais interessante é por que deveria haver quatro tipos, em vez de menos (executando todos os cálculos inteiros com números inteiros de 64 bits e / ou fazendo todos os cálculos de ponto flutuante com valores de ponto flutuante de 64 bits). A razão para o uso de números inteiros de 32 bits é que o Java era executado em muitas plataformas nas quais os tipos de 32 bits poderiam ser utilizados tão rapidamente quanto os tipos de 16 ou 8 bits, mas as operações nos tipos de 64 bits seriam visivelmente Mais devagar.tendo apenas tipos de 32 bits.

Quanto à realização de cálculos de ponto flutuante em valores de 32 bits, as vantagens são um pouco menos claras. Existem algumas plataformas em que uma computação comofloat a=b+c+d;pode ser executado mais rapidamente convertendo todos os operandos para um tipo de precisão mais alta, adicionando-os e depois convertendo o resultado em um número de ponto flutuante de 32 bits para armazenamento. Existem outras plataformas nas quais seria mais eficiente executar todos os cálculos usando valores de ponto flutuante de 32 bits. Os criadores de Java decidiram que todas as plataformas deveriam fazer as coisas da mesma maneira e que deveriam favorecer as plataformas de hardware para as quais os cálculos de ponto flutuante de 32 bits são mais rápidos que os mais longos, mesmo que esse PC severamente degradado tanto a velocidade e precisão da matemática de ponto flutuante em um PC típico, bem como em muitas máquinas sem unidades de ponto flutuante. Observe, btw, que dependendo dos valores de b, c e d, usando cálculos intermediários de maior precisão ao calcular expressões como a mencionada acimafloat a=b+c+d;algumas vezes produz resultados significativamente mais precisos do que seria alcançado com todos os operandos intermediários calculados com floatprecisão, mas algumas vezes produz um valor um pouco menos preciso. De qualquer forma, a Sun decidiu que tudo deveria ser feito da mesma maneira e optou por usar floatvalores de precisão mínima .

Observe que as principais vantagens de tipos de dados menores se tornam aparentes quando um grande número deles é armazenado juntos em uma matriz; mesmo que não houvesse vantagem em ter variáveis ​​individuais de tipos menores que 64 bits, vale a pena ter matrizes que possam armazenar valores menores de maneira mais compacta; ter uma variável local em bytevez de longsalvar sete bytes; tendo uma matriz de 1.000.000 de números, mantenha cada número como um bytee não comolongacena 7.000.000 de bytes. Como cada tipo de matriz precisa apenas suportar algumas operações (principalmente ler um item, armazenar um item, copiar um intervalo de itens dentro de uma matriz ou copiar um intervalo de itens de uma matriz para outra), a complexidade adicional de ter mais tipos de matriz não é tão grave quanto a complexidade de ter mais tipos de valores numéricos discretos diretamente utilizáveis.

supercat
fonte
2

Na verdade, haveria uma pequena vantagem. Se você tem um

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

em uma JVM típica, ele precisa de tanto espaço quanto uma classe que contém uma única int. O consumo de memória é arredondado para um próximo múltiplo de 8 ou 16 bytes (IIRC, configurável); portanto, os casos em que há economia real são bastante raros.

Essa classe seria um pouco mais fácil de usar se os Calendarmétodos correspondentes retornassem a byte. Mas não existem Calendarmétodos, apenas o get(int)que deve retornar um intpor causa de outros campos. Cada operação em tipos menores promove int, então você precisa de muita conversão.

Muito provavelmente, você desistirá e mudará para um intou gravará setters como

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

Então o tipo de DAY_OF_WEEKnão importa, de qualquer maneira.

maaartinus
fonte