primitive tipo - Perché le persone usano ancora i tipi primitivi in Java?




byte variabili (17)

A proposito, Smalltalk ha solo oggetti (non primitivi), e tuttavia hanno ottimizzato i loro piccoli numeri interi (usando non tutti i 32 bit, solo 27 o simili) per non allocare alcuno spazio di heap, ma semplicemente usano uno schema di bit speciale. Anche altri oggetti comuni (true, false, null) avevano schemi di bit speciali qui.

Quindi, almeno sulle JVM a 64 bit (con uno spazio dei nomi puntatore a 64 bit) dovrebbe essere possibile non avere alcun oggetto di Intero, Carattere, Byte, Corto, Booleano, Float (e Piccolo lungo) affatto (a parte questi creati da new ...() esplicito new ...() ), solo modelli di bit speciali, che potrebbero essere manipolati dai normali operatori in modo abbastanza efficiente.

Dal momento che Java 5, abbiamo avuto boxing / unboxing di tipi primitivi in ​​modo che int sia wrapper per essere java.lang.Integer , e così e così via.

Ultimamente vedo molti nuovi progetti Java (che richiedono sicuramente un JRE di almeno la versione 5, se non 6) che usano int piuttosto che java.lang.Integer , sebbene sia molto più comodo usare quest'ultimo, dato che ha alcuni metodi di supporto per la conversione in valori long et al.

Perché alcuni usano ancora i tipi primitivi in ​​Java? C'è qualche vantaggio tangibile?


Puoi davvero immaginare un?

  for (int i=0; i<10000; i++) {
      do something
  }

loop con java.lang.Integer invece? Un java.lang.Integer è immutabile, quindi ogni incremento attorno al ciclo creerebbe un nuovo oggetto java sull'heap, invece di incrementare l'int nello stack con un'unica istruzione JVM. La performance sarebbe diabolica.

Non sarei affatto d'accordo sul fatto che sia molto più comodo usare java.lang.Integer di int. Anzi. Autoboxing significa che puoi usare int dove altrimenti saresti costretto a usare Integer, e il compilatore java si occuperà di inserire il codice per creare il nuovo oggetto Integer per te. Autoboxing consente di utilizzare un int dove è previsto un intero, con il compilatore che inserisce la costruzione dell'oggetto pertinente. In nessun modo rimuove o riduce la necessità di int in primo luogo. Con l'autoboxing ottieni il meglio da entrambi i mondi. Si ottiene automaticamente un intero creato automaticamente quando si ha bisogno di un oggetto java basato su heap e si ottiene la velocità e l'efficienza di un int quando si eseguono calcoli aritmetici e locali.


Sono d'accordo con le risposte precedenti, l'uso di oggetti wrapper primitivi può essere costoso. Tuttavia, se le prestazioni non sono critiche nella tua applicazione, eviti gli overflow quando usi gli oggetti. Per esempio:

long bigNumber = Integer.MAX_VALUE + 2;

Il valore di bigNumber è -2147483647 e ci si aspetterebbe che sia 2147483649. È un errore nel codice che verrebbe risolto eseguendo:

long bigNumber = Integer.MAX_VALUE + 2l; // note that '2' is a long now.

E bigNumber sarebbe 2147483649. Questi tipi di bug a volte sono facili da perdere e possono portare a comportamenti o vulnerabilità sconosciuti (vedi CWE-190 ).

Se si utilizzano oggetti wrapper, il codice equivalente non verrà compilato.

Long bigNumber = Integer.MAX_VALUE + 2; // Not compiling

Quindi è più facile fermare questo tipo di problemi usando gli oggetti wrapper primitivi.

La tua domanda ha già una risposta così, che rispondo solo per aggiungere un po 'più di informazioni non menzionate prima.


I tipi primitivi sono molto più veloci:

int i;
i++;

L'intero (tutti i numeri e anche una stringa) è un tipo immutabile : una volta creato non può essere modificato. Se fossi Integer, allora i++ creerebbe un nuovo oggetto Integer, molto più costoso in termini di memoria e processore.


Perché JAVA esegue tutte le operazioni matematiche in tipi primitivi. Considera questo esempio:

public static int sumEven(List<Integer> li) {
    int sum = 0;
    for (Integer i: li)
        if (i % 2 == 0)
            sum += i;
        return sum;
}

Qui, le operazioni Promemoria e Unario Plus non possono essere applicate al tipo Integer (Riferimento), il compilatore esegue l'unboxing e esegue le operazioni.

Quindi, assicurati quante operazioni di autoboxing e unboxing si verificano nel programma java. Poiché, ci vuole tempo per eseguire queste operazioni.

Generalmente, è meglio mantenere argomenti di tipo Reference e result di tipo primitivo.


In Java efficace di Joshua Bloch, elemento 5: "Evita di creare oggetti non necessari", inserisce il seguente esempio di codice:

public static void main(String[] args) {
    Long sum = 0L; // uses Long, not long
    for (long i = 0; i <= Integer.MAX_VALUE; i++) {
        sum += i;
    }
    System.out.println(sum);
}

e ci vogliono 43 secondi per correre. Portare il Lungo nel primitivo lo porta a 6,8 secondi ... Se questo è un indizio del perché usiamo le primitive.

Anche la mancanza di uguaglianza dei valori nativi è una preoccupazione ( .equals() è piuttosto prolissa rispetto a == )

per biziclop:

class Biziclop {

    public static void main(String[] args) {
        System.out.println(new Integer(5) == new Integer(5));
        System.out.println(new Integer(500) == new Integer(500));

        System.out.println(Integer.valueOf(5) == Integer.valueOf(5));
        System.out.println(Integer.valueOf(500) == Integer.valueOf(500));
    }
}

Risultati in:

false
false
true
false

EDIT Perché (3) restituisce true e (4) restituisce false ?

Perché sono due oggetti diversi. I 256 interi più vicini allo zero [-128; 127] sono memorizzati nella cache dalla JVM, quindi restituiscono lo stesso oggetto per quelli. Oltre questo intervallo, tuttavia, non vengono memorizzati nella cache, quindi viene creato un nuovo oggetto. Per rendere le cose più complicate, il JLS richiede che vengano salvati almeno 256 flyweights. Gli implementatori di JVM possono aggiungerne altri se desiderano, il che significa che potrebbero essere eseguiti su un sistema in cui i 1024 più vicini vengono memorizzati nella cache e tutti restituiscono true ... #awkward


1) Hai bisogno di primitive per fare operazioni matematiche 2) I primitivi richiedono meno memoria come risposta sopra e prestazioni migliori

Dovresti chiedere perché è richiesto il tipo di classe / oggetto

La ragione per avere il tipo di oggetto è semplificarci la vita quando gestiamo le raccolte. Le primitive non possono essere aggiunte direttamente a List / Map, ma è necessario scrivere una classe wrapper. Il tipo di classi Readymade Integer ti aiuta qui in più ha molti metodi di utilità come Integer.pareseInt (str)


Prima di tutto, abitudine. Se hai codificato in Java per otto anni, accumuli una considerevole quantità di inerzia. Perché cambiare se non ci sono motivi validi per farlo? Non è come se usare i primitivi inscatolati abbia qualche vantaggio in più.

L'altro motivo è affermare che null non è un'opzione valida. Sarebbe inutile e fuorviante dichiarare la somma di due numeri o una variabile di ciclo come Integer .

C'è anche l'aspetto delle prestazioni, mentre la differenza di prestazioni non è critica in molti casi (anche se è pessima), a nessuno piace scrivere codice che possa essere scritto altrettanto facilmente in un modo più veloce che siamo già abituato a.


int loops = 100000000;

long start = System.currentTimeMillis();
for (Long l = new Long(0); l<loops;l++) {
    //System.out.println("Long: "+l);
}
System.out.println("Milliseconds taken to loop '"+loops+"' times around Long: "+ (System.currentTimeMillis()- start));

start = System.currentTimeMillis();
for (long l = 0; l<loops;l++) {
    //System.out.println("long: "+l);
}
System.out.println("Milliseconds taken to loop '"+loops+"' times around long: "+ (System.currentTimeMillis()- start));

Millisecondi portati a ciclo '100000000' volte su Long: 468

Millisecondi portati a ciclo '100000000' volte intorno a lungo: 31

In una nota a margine, non mi dispiacerebbe vedere qualcosa di simile trovare in Java.

Integer loop1 = new Integer(0);
for (loop1.lessThan(1000)) {
   ...
}

Dove il ciclo for incrementa automaticamente loop1 da 0 a 1000 o

Integer loop1 = new Integer(1000);
for (loop1.greaterThan(0)) {
   ...
}

Dove il ciclo for decrementa automaticamente il loop1 da 1000 a 0.


Oltre a ciò che altri hanno detto, le variabili locali primitive non vengono allocate dall'heap, ma invece nello stack. Ma gli oggetti sono allocati dall'heap e quindi devono essere raccolti.


I tipi di box hanno prestazioni peggiori e richiedono più memoria.


Gli oggetti sono molto più pesanti dei tipi primitivi, quindi i tipi primitivi sono molto più efficienti rispetto alle istanze delle classi wrapper.

I tipi primitivi sono molto semplici: per esempio un int è 32 bit e occupa esattamente 32 bit in memoria e può essere manipolato direttamente. Un oggetto intero è un oggetto completo, che (come qualsiasi oggetto) deve essere memorizzato nell'heap e accessibile solo tramite un riferimento (puntatore) ad esso. Molto probabilmente richiede anche più di 32 bit (4 byte) di memoria.

Detto questo, il fatto che Java abbia una distinzione tra tipi primitivi e non primitivi è anche un segno dell'età del linguaggio di programmazione Java. I nuovi linguaggi di programmazione non hanno questa distinzione; il compilatore di un tale linguaggio è abbastanza intelligente da capire da solo se si utilizzano valori semplici o oggetti più complessi.

Ad esempio, in Scala non ci sono tipi primitivi; c'è una classe Int per interi, e una Int è un oggetto reale (che puoi usare su metodi, ecc.). Quando il compilatore compila il tuo codice, utilizza inti primitivi dietro le quinte, quindi usare un Int è altrettanto efficace quanto usare un int primitivo in Java.


Un paio di ragioni per non liberarsi dei primitivi:

  • Compatibilità all'indietro.

Se è eliminato, qualsiasi vecchio programma non verrebbe nemmeno eseguito.

  • Riscrittura JVM.

L'intera JVM dovrebbe essere riscritta per supportare questa nuova cosa.

  • Più grande spazio di memoria.

Dovresti memorizzare il valore e il riferimento, che utilizza più memoria. Se si dispone di una vasta gamma di byte, l'utilizzo di byte è significativamente inferiore rispetto all'utilizzo di Byte .

  • Problemi con puntatori nulli.

Se dichiaro che int i facendo cose con i avrei riscontrato alcun problema, ma dichiarare Integer i e poi fare lo stesso risulterebbe in un NPE.

  • Problemi di uguaglianza.

Considera questo codice:

Integer i1 = 5;
Integer i2 = 5;

i1 == i2; // Currently would be false.

Sarebbe falso Gli operatori dovrebbero essere sovraccaricati e ciò comporterebbe una grande riscrittura delle cose.

  • Lento

I wrapper di oggetti sono molto più lenti delle loro controparti primitive.


Non posso credere che nessuno abbia menzionato ciò che ritengo sia la ragione più importante: "int" è così, molto più facile da digitare di "Integer". Penso che la gente sottovaluti l'importanza di una sintassi sintetica. Le prestazioni non sono davvero un motivo per evitarle perché la maggior parte delle volte in cui si utilizzano numeri è negli indici di loop e l'incremento e il confronto di tali costi non si risolve in alcun ciclo non banale (indipendentemente dal fatto che si utilizzi int o Integer).

L'altro motivo è dato dal fatto che è possibile ottenere NPE ma è estremamente facile da evitare con i tipi di box (e si garantisce che sia evitato finché si inizializzano sempre su valori non nulli).

L'altra ragione era che (new Long (1000)) == (new Long (1000)) è falso, ma questo è solo un altro modo per dire che ".equals" non ha supporto sintattico per i tipi di box (a differenza degli operatori <,> , =, ecc.), quindi torniamo alla ragione "sintassi più semplice".

Penso che l'esempio di loop non primitivo di Steve Yegge illustri molto bene il mio punto: http://sites.google.com/site/steveyegge2/language-trickery-and-ejb

Pensa a questo: con che frequenza usi i tipi di funzione in linguaggi che hanno una buona sintassi per loro (come qualsiasi linguaggio funzionale, python, ruby ​​e anche C) rispetto a java dove devi simularli usando interfacce come Runnable e Callable e classi senza nome.


Tipi primitivi:

int x = 1000;
int y = 1000;

Ora valuta:

x == y

È true Difficilmente sorprendente. Ora prova i tipi in scatola:

Integer x = 1000;
Integer y = 1000;

Ora valuta:

x == y

È false . Probabilmente. Dipende dal runtime. Questa è una ragione sufficiente?


I tipi primitivi presentano molti vantaggi: - Codice più semplice da scrivere - Le prestazioni sono migliori poiché non si crea un'istanza di un oggetto per la variabile - Dal momento che non rappresentano un riferimento a un oggetto, non è necessario controllare i valori null - Utilizzare i tipi primitivi a meno che bisogno di sfruttare le funzionalità di boxe.


Invece di convertire ogni data, si utilizza il seguente codice

long difference = (sDt4.getTime() - sDt3.getTime()) / 1000;
System.out.println(difference);

E vedi il risultato è:

1




java primitive primitive-types autoboxing jdk1.5