Perché l'API Java utilizza int
, quando short
o addirittura byte
sarebbe sufficiente?
Esempio: il DAY_OF_WEEK
campo nella classe Calendar
utilizza int
.
Se la differenza è troppo minima, perché esistono questi tipi di dati ( short
, int
)?
Perché l'API Java utilizza int
, quando short
o addirittura byte
sarebbe sufficiente?
Esempio: il DAY_OF_WEEK
campo nella classe Calendar
utilizza int
.
Se la differenza è troppo minima, perché esistono questi tipi di dati ( short
, int
)?
Risposte:
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 int
e il tipo long
e non per i tipi più piccoli.
(A parte: i tipi più piccoli ( byte
e 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 long
valore 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_WEEK
come 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" double
e long
occupano due slot). Quindi dichiarando esplicitamente un campo come short
o byte
non si salverebbe neanche memoria.
int
. Se hai un riferimento a un'altra implementazione, aggiornerei la risposta e inserisco il link di conseguenza.
(Quasi) Tutte le operazioni attive byte
, short
le 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. byte
e short
complicherebbe 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 Calendar
classe 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
, short
e altre primitive non farà alcuna differenza a tutti. Inoltre, molte implementazioni Java allineano le variabili * (e gli oggetti).
* byte
e short
occupano lo stesso spazio come int
se 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, byte
prendere 1 byte, short
prendere 2 byte e int
prendere 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.
Perché le operazioni aritmetiche sono più semplici quando si usano numeri interi rispetto ai cortocircuiti. Supponiamo che le costanti siano state effettivamente modellate da short
valori. 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 int
valori 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 int
valori 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 int
in short
sarebbe sicuro 40 * 16 bit = 80 byte
. Vedi questa risposta per ulteriori riferimenti.
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.
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 float
precisione, 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 float
valori 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 byte
piuttosto che un long
salva sette byte; avere una matrice di 1.000.000 di numeri contiene ogni numero come un byte
anziché unlong
onde 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.
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 Calendar
metodi corrispondenti restituissero a byte
. Ma non esistono tali Calendar
metodi, ma solo quelli get(int)
che devono restituire una int
causa 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 int
o scriverai setter come
void setDayOfWeek(int dayOfWeek) {
this.dayOfWeek = checkedCastToByte(dayOfWeek);
}
Quindi il tipo di DAY_OF_WEEK
non importa, comunque.