I tipi di dati Java possono essere suddivisi condizionatamente in due blocchi: primitivo e riferimento (classi). Esistono diversi tipi di dati primitivi in ​​Java, come numeri interi ( byte , short , int , long ), numeri in virgola mobile ( float , double ), tipo di dati logici ( boolean ) e tipo di dati carattere ( char ). Probabilmente sai già che ogni tipo di dati primitivo ha la propria classe wrapper. Un tipo di dati di riferimento che "avvolge" o trasforma il suo fratellino primitivo in un oggetto Java. Integer è una classe wrapper per il suo fratello primitivo chiamato int. Intero in inglese significa un numero intero. Possono essere positivi, negativi o 0. Sfortunatamente, Integer in Java non significa un numero intero. Un intero in Java è un numero intero che sta in 32 bit. Se desideri un numero maggiore, puoi utilizzare i numeri Java Long . Hanno 64 bit a loro disposizione. Se sei abbastanza sfortunato da aver bisogno di un numero ancora più grande, Java ti copre con BigInteger .

Lavorare con numeri interi

Come classe wrapper, Integer fornisce vari metodi per lavorare con int , oltre a una serie di metodi per convertire int in String e String in int . La classe ha due costruttori:
  • public Integer(int i) , dove i è un valore primitivo da inizializzare. Questo crea un oggetto Integer che viene inizializzato con il valore int .

  • public Integer(String s) genera NumberFormatException . Ecco una rappresentazione di stringa del valore int . Questo costruttore crea un oggetto Integer che è stato inizializzato con il valore int fornito dalla rappresentazione di stringa .

Creazione di oggetti interi

Esistono diverse opzioni per la creazione di oggetti Integer . Uno dei più utilizzati è quello più semplice. Ecco un esempio:
Integer myInteger = 5;
L'inizializzazione della variabile Integer in questo caso è simile all'inizializzazione della variabile int primitiva . A proposito, puoi inizializzare una variabile intera con il valore di un int . Ecco un esempio:
int myInt = 5;
Integer myInteger = myInt;
System.out.println(myInteger);
L'output qui è:
5
In effetti, qui possiamo osservare l'auto-imballaggio. Inoltre possiamo creare un oggetto Integer proprio come qualsiasi altro oggetto utilizzando un costruttore e una nuova parola chiave:
Integer myInteger = new Integer(5);
Puoi fare con la variabile Integer lo stesso che con int (aggiungi, sottrai, moltiplica, dividi, incrementa, decrementa). Tuttavia, è importante ricordare che Integer è un tipo di dati di riferimento e una variabile di questo tipo può essere nulla. In questo caso è meglio astenersi da tali operazioni.
Integer myInteger1  = null;
Integer myInteger2 = myInteger1 + 5;
Qui otterremo un'eccezione:
Eccezione nel thread "main" java.lang.NullPointerException"

Costanti di classe intera

La classe Integer fornisce varie costanti e metodi per lavorare con i numeri interi. Eccoli:
  • DIMENSIONE indica il numero di bit nel sistema numerico a due cifre occupato dal tipo int

  • BYTES è il numero di byte nel sistema numerico a due cifre occupati dal tipo int

  • MAX_VALUE è il valore massimo che il tipo int può contenere

  • MIN_VALUE è il valore minimo che il tipo int può contenere

  • TYPE restituisce un oggetto di tipo Class dal tipo int

Metodi più utili della classe intera

Diamo ora uno sguardo ai metodi più utilizzati della classe Integer . I più popolari, presumo, sono i metodi per convertire un numero da un String o viceversa.
  • static int parseInt(String s) questo metodo converte String in int . Se la conversione non è possibile, verrà lanciata NumberFormatException .

  • static int parseInt(String s, int radix) questo metodo converte anche il parametro s in un int . Il parametro radice indica il sistema numerico s originariamente scritto.

Oltre a parseInt , esiste anche un metodo valueOf molto simile in diverse varianti. Tuttavia, il risultato di valueOf sarà Integer e parseInt sarà int .
  • static Integer valueOf(int i) restituisce un intero il cui valore è i ;

  • static Integer valueOf(String s) funziona come parseInt(String s) , ma il risultato sarà Integer , non int ;

  • static Integer valueOf(String s, int radix) funziona allo stesso modo di parseInt(String s, int radix) , ma il risultato è un Integer , non un int .

C'è qualche problema con la classe Integer? Oh sì, c'è...

Quindi ci sono due tipi di numeri interi (che rientrano in 32 bit) in Java: int e Integer . Per comprendere le specifiche di ciascuno di essi dobbiamo sapere quanto segue sul modello di memoria JVM: tutto ciò che dichiari viene archiviato nella Stack Memory (stack JVM specifico per ciascun thread) o nello spazio Heap. I tipi primitivi ( int , long , float , boolean , double , char , byte , ecc.) sono archiviati nella memoria Stack. Tutti gli oggetti e gli array sono archiviati nello spazio heap. I riferimenti a questi oggetti e array necessari per i metodi sono archiviati in Stack. COSÌ. Perché ci interessa? Bene, vedi, Stack è più piccolo di Heap (un contro), ma è molto più veloce allocare valori in Stack che in Heap (un professionista). Cominciamo con un tipo primitivo int . Occupa esattamente 32 bit. Questo è 32/8=4 byte. Perché è un tipo primitivo. Consideriamo ora Integer . È un oggetto, con spese generali e allineamenti aggiuntivi. Ho usato una libreria jol per misurarne le dimensioni:
public static void main(String[] args) {
 	System.out.println(ClassLayout.parseInstance(Integer.valueOf(1)).toPrintable());
}
e si è scoperto che occupava 16 byte:
java.lang.Integer componenti interni dell'oggetto: OFF SZ TIPO DESCRIZIONE VALORE 0 8 (intestazione oggetto: mark) 0x000000748c90e301 (hash: 0x748c90e3; age: 0) 8 4 (intestazione oggetto: class) 0x000492a0 12 4 int Integer.value 1 Dimensione istanza: 16 byte
Che cosa?! Si tratta di 4 volte più memoria! Ma non fermiamoci qui. Come sviluppatori Java normalmente non smettiamo di usare un singolo numero intero. Quello che vogliamo veramente è usarne molti. Come in una sequenza. Ad esempio, in un array. O una lista. Gli array sono archiviati nell'heap, come lo sono gli elenchi. Pertanto, l'allocazione dovrebbe richiedere all'incirca lo stesso tempo. Giusto? Ma cosa succede se dobbiamo allocare più memoria? Controlliamo quanto spazio occupa un array di 1000 valori int primitivi:
public static void main(String[] args) {
    	int[] array = new int[1000];
    	for (int i = 0; i < 1000; i++) array[i] = i;                System.out.println(ClassLayout.parseInstance(array).toPrintable());
}
E il risultato è 4016 byte:
OFF SZ TIPO DESCRIZIONE VALORE 0 8 (intestazione oggetto: mark) 0x0000000000000001 (non biasable; età: 0) 8 4 (intestazione oggetto: class) 0x00006c38 12 4 (lunghezza array) 1000 12 4 (gap di allineamento/riempimento) 16 4000 int [I.<elementi> N/D Dimensioni istanza: 4016 byte Perdite di spazio: 4 byte interni + 0 byte esterni = 4 byte totali
OK, ha senso, considerando che un singolo int richiede 4 byte. Che ne dici di un ArrayList<Integer> di 1000 numeri interi ? Diamo un'occhiata:
public static void main(String[] args) {
	List<Integer> list = new ArrayList<>(1000);
	for (int i = 0; i < 1000; i++) list.add(i);
      System.out.println(GraphLayout.parseInstance(list).toFootprint());
}
E il risultato è 20040 byte (di nuovo, 4 volte di più!):
java.util.ArrayList@66d3c617d impronta: COUNT AVG SUM DESCRIZIONE 1 4016 4016 [Ljava.lang.Object; 1000 16 16000 java.lang.Integer 1 24 24 java.util.ArrayList 1002 20040 (totale)
Pertanto, ArrayList<Integer> occupa 4 volte più spazio di memoria. Questo non è buono. Tuttavia, le liste sono più semplici perché possiamo aggiungere ed eliminare elementi! Oh Java... Perché hai bisogno di inscatolare tutto?! Ma, mi sto dimenticando, Java è fantastico e la sua grandezza risiede nell'abbondanza di librerie open source che possiamo utilizzare! Trove4j è uno di questi. Ha TIntArrayList che internamente ha un dato int[] . Misuriamo le sue dimensioni:
public static void main(String[] args) {
	TIntList list = new TIntArrayList(1000);
	for (int i = 0; i < 1000; i++) list.add(i);
	System.out.println(GraphLayout.parseInstance(list).toFootprint());
}
E il risultato è 4040 byte (quasi uguale a int[] !):
gnu.trove.list.array.TIntArrayList@7440e464d impronta: COUNT AVG SUM DESCRIZIONE 1 4016 4016 [I 1 24 24 gnu.trove.list.array.TIntArrayList 2 4040 (totale)
Quindi, alla fine, possiamo avere il meglio di entrambi i mondi! Liste di numeri interi che occupano 4 volte meno spazio. E questo non coinvolge le istanze di Integer . Solo int . Noi sviluppatori Java ci preoccupiamo davvero della memoria... Ma ci preoccupiamo anche delle prestazioni. Esiste una meravigliosa libreria di microbenchmarking con un nome modesto jmh che ci consente di misurare le prestazioni del codice. Per prima cosa confrontiamo le prestazioni del calcolo della somma di due numeri interi casuali, racchiusi o meno: La configurazione per jmh è la seguente:
benchmark {
	configurations {
    	main {
        	warmups = 5 // number of warmup iterations
        	iterations = 50 // number of iterations
        	iterationTime = 500 // time in seconds per iteration
        	iterationTimeUnit = "ns" // time unit for iterationTime
I parametri di riferimento:
private static final Random random = new Random();

@Benchmark
public int testPrimitiveIntegersSum() {
	int a = random.nextInt();
	int b = random.nextInt();
	return a + b;
}

@Benchmark
public Integer testBoxedIntegersSum() {
	Integer a = random.nextInt();
	Integer b = random.nextInt();
	return a + b;
}
I risultati:
principale: test.SampleJavaBenchmark.testBoxedIntegersSum 5693337.344 ±(99.9%) 1198774.178 ops/s [Media] (min, avg, max) = (1092314.989, 5693337.344, 12001683.428), stdev = 24215 83.144 IC (99,9%): [4494563.166, 6892111.522] (presuppone una distribuzione normale) main: test.SampleJavaBenchmark.testPrimitiveIntegersSum 15295010.959 ±(99.9%) 2555447.456 ops/s [Media] (min, avg, max) = (4560097.059, 15295010.959, 24283809.447), dev.st = 5162130.283 CI (99,9%): [12739563.502, 17850458.415] (presuppone una distribuzione normale)
Quindi, in media, l'allocazione e la somma degli interi primitivi è più del doppio più veloce di quella degli interi inscatolati. Ora confrontiamo le prestazioni di creazione e calcolo della somma delle raccolte (o array di 1000 int di numeri interi):
@Benchmark
public int testPrimitiveArray() {
	int[] array = new int[1000];
	for (int i = 0; i < 1000; i++) array[i] = i;
	int sum = 0;
	for (int x : array) sum += x;
	return sum;
}
11933.545 ops/s [Average]


@Benchmark
public int testBoxesArray() {
	Integer[] array = new Integer[1000];
	for (int i = 0; i < 1000; i++) array[i] = i;
	int sum = 0;
	for (int x : array) sum += x;
	return sum;
}
2733.312 ops/s [Average]


@Benchmark
public int testList() {
	List<Integer> list = new ArrayList<>(1000);
	for (int i = 0; i < 1000; i++) list.add(i);
	int sum = 0;
	for (int x : list) sum += x;
	return sum;
}
2086.379 ops/s [Average]


@Benchmark
public int testTroveIntList() {
	TIntList list = new TIntArrayList(1000);
	for (int i = 0; i < 1000; i++) list.add(i);
	int sum = 0;
	for (int i = 0; i < 1000; i++) sum += list.get(i);
	return sum;
}
5727.979 ops/s [Average]
I risultati: l'array di primitive è più di 4 volte più veloce dell'array di valori boxed ( Integer s); quasi sei volte più veloce di ArrayList di valori boxed ( Integer s); e due volte più veloce di un TIntArrayList (che in realtà decora una serie di int primitivi). Pertanto, se hai bisogno di una struttura dati per memorizzare una raccolta di valori interi e la sua dimensione non cambierà, usa un int[] ; se la dimensione cambierà, potresti voler utilizzare la libreria tove4j con TIntArrayList . E qui arriva la fine del mio saggio in cui spiego gli svantaggi dell'utilizzo del tipo Integer . Esistono alcuni metodi statici interessanti di Integer , di cui dovrei parlare prima di finire. public static Integer getInteger(String nm, int val) non fa quello che si potrebbe pensare, ma recupera un valore intero di una proprietà di sistema. Val è l'impostazione predefinita nel caso in cui questa proprietà non sia impostata. public static String toBinaryString(int i) restituisce una stringa con una rappresentazione binaria di un numero. Esistono metodi per il recupero di rappresentazioni in base 16 ( toHexString ) e in base 8 ( toOctalString ). Esiste un metodo per analizzare una String in un int . Anche se la stringa è una rappresentazione non basata su radice 10. Ecco alcuni esempi: Integer.parseInt("-FF", 16) restituisce -255 Integer.parseInt("+42", 10) restituisce 42 Integer.parseInt("1100110", 2) restituisce 102