Perché l'API Java utilizza int anziché short o byte?


137

Perché l'API Java utilizza int, quando shorto addirittura bytesarebbe sufficiente?

Esempio: il DAY_OF_WEEKcampo nella classe Calendarutilizza int.

Se la differenza è troppo minima, perché esistono questi tipi di dati ( short, int)?

Risposte:


166

Alcuni dei motivi sono già stati indicati. Ad esempio, il fatto che "... (quasi) tutte le operazioni su byte, breve promuoverà queste primitive in int" . Tuttavia, l'ovvia domanda successiva sarebbe: PERCHÉ vengono promossi questi tipi int?

Quindi, per andare ad un livello più profondo: la risposta potrebbe essere semplicemente correlata al set di istruzioni della macchina virtuale Java. Come riassunto nella Tabella nelle Specifiche della macchina virtuale Java , tutte le operazioni aritmetiche integrali, come l'aggiunta, la divisione e altre, sono disponibili solo per il tipo inte il tipo longe non per i tipi più piccoli.

(A parte: i tipi più piccoli ( bytee short) sono essenzialmente destinati solo agli array . Un array come new byte[1000]richiederà 1000 byte e un array come new int[1000]richiederà 4000 byte)

Ora, naturalmente, si potrebbe dire che "... l'ovvia domanda successiva sarebbe: PERCHÉ queste istruzioni sono offerte solo per int(e long)?" .

Un motivo è menzionato nelle specifiche JVM sopra menzionate:

Se ogni istruzione digitata supportasse tutti i tipi di dati di runtime della Java Virtual Machine, ci sarebbero più istruzioni di quante potrebbero essere rappresentate in un byte

Inoltre, la Java Virtual Machine può essere considerata come un'astrazione di un vero processore. E introdurre un'unità logica aritmetica dedicata per tipi più piccoli non varrebbe la pena: avrebbe bisogno di transistor aggiuntivi, ma poteva comunque eseguire solo un'aggiunta in un ciclo di clock. L'architettura dominante al momento della progettazione di JVM era di 32 bit, perfetta per un 32 bit int. (Le operazioni che comportano un longvalore di 64 bit sono implementate come un caso speciale).

(Nota: l'ultimo paragrafo è un po 'troppo semplificato, considerando l'eventuale vettorializzazione ecc., Ma dovrebbe dare l'idea di base senza approfondire troppo gli argomenti di progettazione del processore)


EDIT: un breve addendum, focalizzato sull'esempio della domanda, ma in senso più generale: si potrebbe anche chiedere se non sarebbe utile memorizzare campi usando i tipi più piccoli. Ad esempio, si potrebbe pensare che la memoria possa essere salvata memorizzandola Calendar.DAY_OF_WEEKcome a byte. Ma qui entra in gioco il formato file di classe Java: tutti i campi in un file di classe occupano almeno uno "slot", che ha le dimensioni di uno int(32 bit). (I campi "larghi" doublee longoccupano due slot). Quindi dichiarando esplicitamente un campo come shorto bytenon si salverebbe neanche memoria.


Immagino che la logica del perché gli operandi sono promossi in int è anche correlata alla logica usata in C e C ++
Shafik Yaghmour,

@ Marco13 "Dichiarare in modo esplicito un campo come corto o byte non salverebbe neppure memoria." è vero? Non penso sia corretto.
ACV,

@ACV A rigor di termini, un'implementazione potrebbe scegliere di memorizzare una forma più compatta, ma il formato che viene esposto "virtualmente" (cioè dalla macchina virtuale) tratterà i valori come almeno della dimensione di int. Se hai un riferimento a un'altra implementazione, aggiornerei la risposta e inserisco il link di conseguenza.
Marco13

40

(Quasi) Tutte le operazioni attive byte, shortle promuoveranno int, ad esempio, non è possibile scrivere:

short x = 1;
short y = 2;

short z = x + y; //error

L'aritmetica è più semplice e intuitiva durante l'utilizzo int, non è necessario eseguire il cast.

In termini di spazio, fa una molto piccola differenza. bytee shortcomplicherebbe le cose, non credo che questa micro ottimizzazione ne valga la pena poiché stiamo parlando di un numero fisso di variabili.

byteè pertinente e utile quando si programma per dispositivi integrati o si tratta di file / reti. Anche queste primitive sono limitate, e se i calcoli potessero superare i loro limiti in futuro? Prova a pensare a un'estensione per Calendarclasse che potrebbe evolvere numeri più grandi.

Si noti inoltre che in processori a 64 bit, i locali verranno salvati nei registri e non useranno tutte le risorse, in modo da utilizzare int, shorte altre primitive non farà alcuna differenza a tutti. Inoltre, molte implementazioni Java allineano le variabili * (e gli oggetti).


* byte e shortoccupano lo stesso spazio come intse fossero variabili locali , variabili di classe o persino variabili di istanza . Perché? Perché nella (maggior parte dei) sistemi di computer, gli indirizzi delle variabili sono allineati , quindi ad esempio se usi un singolo byte, finirai con due byte, uno per la variabile stessa e un altro per il riempimento.

D'altra parte, negli array, byteprendere 1 byte, shortprendere 2 byte e intprendere quattro byte, perché negli array deve essere allineato solo l'inizio e forse la sua fine. Questo farà la differenza nel caso tu voglia usare, per esempio System.arraycopy(), allora noterai davvero una differenza di prestazioni.


1
Curiosità: se usi i modificatori finali per entrambi i valori, funzionerà. :)
alexander

7

Perché le operazioni aritmetiche sono più semplici quando si usano numeri interi rispetto ai cortocircuiti. Supponiamo che le costanti siano state effettivamente modellate da shortvalori. Quindi dovresti usare l'API in questo modo:

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

Notare il casting esplicito. I valori brevi vengono implicitamente promossi a intvalori quando vengono utilizzati in operazioni aritmetiche. (Sullo stack di operandi, i cortometraggi sono persino espressi come in.) Questo sarebbe piuttosto ingombrante da usare, motivo per cui i intvalori sono spesso preferiti per le costanti.

Rispetto a ciò, il guadagno nell'efficienza di archiviazione è minimo perché esiste solo un numero fisso di tali costanti. Stiamo parlando di 40 costanti. Cambiare il loro spazio di archiviazione da intin shortsarebbe sicuro 40 * 16 bit = 80 byte. Vedi questa risposta per ulteriori riferimenti.


5

Se hai usato la filosofia in cui le costanti integrali sono memorizzate nel tipo più piccolo in cui si adattano, allora Java avrebbe un problema serio: ogni volta che i programmatori scrivono codice usando costanti integrali, devono prestare molta attenzione al loro codice per verificare se il tipo di le costanti contano, e in tal caso cercare il tipo nella documentazione e / o fare qualunque tipo di conversione sia necessaria.

Quindi, ora che abbiamo delineato un problema serio, quali benefici potresti sperare di ottenere con quella filosofia? Non sarei sorpreso se l' unico effetto osservabile in fase di runtime di quel cambiamento fosse il tipo che ottieni quando guardi la costante attraverso la riflessione. (e, naturalmente, qualunque errore venga introdotto da programmatori pigri / inconsapevoli che non tengono correttamente conto dei tipi di costanti)

Pesare i pro e i contro è molto semplice: è una cattiva filosofia.


4

La complessità di progettazione di una macchina virtuale dipende da quanti tipi di operazioni può eseguire. È più semplice avere quattro implementazioni di un'istruzione come "moltiplicare" - una per ogni intero a 32 bit, intero a 64 bit, virgola mobile a 32 bit e virgola mobile a 64 bit - rispetto ad avere, inoltre a quanto sopra, versioni anche per i tipi numerici più piccoli. Una domanda di progettazione più interessante è perché dovrebbero esserci quattro tipi, anziché un numero inferiore (eseguendo tutti i calcoli dei numeri interi con numeri interi a 64 bit e / o eseguendo tutti i calcoli in virgola mobile con valori in virgola mobile a 64 bit). La ragione per usare numeri interi a 32 bit è che ci si aspettava che Java funzionasse su molte piattaforme in cui i tipi a 32 bit potevano essere applicati con la stessa rapidità dei tipi a 16 o 8 bit, ma le operazioni sui tipi a 64 bit sarebbero notevolmente Più lentamente.avendo solo tipi a 32 bit.

Per quanto riguarda l'esecuzione di calcoli in virgola mobile su valori a 32 bit, i vantaggi sono un po 'meno chiari. Ci sono alcune piattaforme in cui piace un calcolofloat a=b+c+d;potrebbe essere eseguito più rapidamente convertendo tutti gli operandi in un tipo di precisione più elevata, aggiungendoli e quindi riconvertendo il risultato in un numero a virgola mobile a 32 bit per l'archiviazione. Esistono altre piattaforme in cui sarebbe più efficiente eseguire tutti i calcoli utilizzando valori a virgola mobile a 32 bit. I creatori di Java hanno deciso che tutte le piattaforme dovrebbero essere obbligate a fare le cose allo stesso modo e che dovrebbero favorire le piattaforme hardware per le quali i calcoli a virgola mobile a 32 bit sono più veloci di quelli più lunghi, anche se questo PC gravemente degradato sia la velocità e precisione della matematica in virgola mobile su un tipico PC, nonché su molte macchine senza unità a virgola mobile. Si noti, tra l'altro, che a seconda dei valori di b, c e d, utilizzando calcoli intermedi di maggiore precisione quando si calcolano espressioni come quelle sopra menzionatefloat a=b+c+d;a volte produrrà risultati significativamente più accurati di quanto sarebbe possibile ottenere con tutti gli operandi intermedi calcolati con floatprecisione, ma a volte produrrà un valore un po 'meno accurato. In ogni caso, Sun ha deciso di fare tutto allo stesso modo e ha optato per l'utilizzo di floatvalori di precisione minima .

Si noti che i vantaggi principali dei tipi di dati più piccoli diventano evidenti quando un gran numero di essi viene archiviato insieme in un array; anche se non vi era alcun vantaggio nell'avere singole variabili di tipi inferiori a 64 bit, vale la pena disporre di array in grado di memorizzare valori più piccoli in modo più compatto; avere una variabile locale essere un bytepiuttosto che un longsalva sette byte; avere una matrice di 1.000.000 di numeri contiene ogni numero come un byteanziché unlongonde 7.000.000 di byte. Poiché ogni tipo di array deve supportare solo alcune operazioni (in particolare leggere un elemento, archiviare un elemento, copiare un intervallo di elementi all'interno di un array o copiare un intervallo di elementi da un array a un altro), la complessità aggiunta di avere più i tipi di array non sono così gravi come la complessità di avere più tipi di valori numerici discreti direttamente utilizzabili.


2

In realtà, ci sarebbe un piccolo vantaggio. Se hai un

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

quindi su una JVM tipica ha bisogno di più spazio di una classe che contiene un singolo int. Il consumo di memoria viene arrotondato a un multiplo successivo di 8 o 16 byte (IIRC, che è configurabile), quindi i casi in cui si verifica un vero salvataggio sono piuttosto rari.

Questa classe sarebbe leggermente più facile da usare se i Calendarmetodi corrispondenti restituissero a byte. Ma non esistono tali Calendarmetodi, ma solo quelli get(int)che devono restituire una intcausa di altri campi. Ogni operazione su tipi più piccoli promuove int, quindi è necessario un sacco di casting.

Molto probabilmente, ti arrenderai e passerai a un into scriverai setter come

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

Quindi il tipo di DAY_OF_WEEKnon importa, comunque.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.