CodeGym /Blog Java /Random-FR /Classe Java.lang.Integer
John Squirrels
Niveau 41
San Francisco

Classe Java.lang.Integer

Publié dans le groupe Random-FR
Les types de données Java peuvent être conditionnellement divisés en deux blocs : primitif et référence (classes). Il existe plusieurs types de données primitifs en Java, tels que les entiers ( byte , short , int , long ), les nombres à virgule flottante ( float , double ), le type de données logique ( boolean ) et le type de données caractère ( char ). Vous savez probablement déjà que chaque type de données primitif possède sa propre classe wrapper. Un type de données de référence qui « enveloppe » ou transforme son petit frère primitif dans un objet Java. Integer est une classe wrapper pour son frère primitif nommé int. En anglais, Integer signifie un nombre entier. Ils peuvent être positifs, négatifs ou 0. Malheureusement, Integer en Java ne signifie pas un nombre entier. Un entier en Java est un nombre entier qui tient sur 32 bits. Si vous souhaitez un nombre plus grand, vous pouvez utiliser les nombres Java Long . Ils disposent de 64 bits. Si vous n'avez pas la chance d'avoir besoin d'un nombre encore plus grand, Java vous propose BigInteger .

Travailler avec un nombre entier

En tant que classe wrapper, Integer fournit diverses méthodes pour travailler avec int , ainsi qu'un certain nombre de méthodes pour convertir int en String et String en int . La classe a deux constructeurs :
  • public Integer(int i) , où i est une valeur primitive à initialiser. Celui-ci crée un objet Integer initialisé avec la valeur int .

  • public Integer(String s) lance NumberFormatException . Ici , s est une représentation sous forme de chaîne de la valeur int . Ce constructeur crée un objet Integer qui a été initialisé avec la valeur int fournie par la représentation sous forme de chaîne .

Création d'objet entier

Il existe différentes options de création d'objets Integer . L’un des plus couramment utilisés est le plus simple. Voici un exemple:

Integer myInteger = 5;
L'initialisation de la variable Integer dans ce cas est similaire à l'initialisation de la variable int primitive . À propos, vous pouvez initialiser une variable Integer avec la valeur d'un int . Voici un exemple:

int myInt = 5;
Integer myInteger = myInt;
System.out.println(myInteger); 
Le résultat ici est :
5
En fait, nous pouvons ici observer un auto-packing. Nous pouvons également créer un objet Integer comme n'importe quel autre objet en utilisant un constructeur et un nouveau mot-clé :

Integer myInteger = new Integer(5);
Vous pouvez faire avec la variable Integer la même chose qu'avec int (ajouter, soustraire, multiplier, diviser, incrémenter, décrémenter). Cependant, il est important de se rappeler qu’Integer est un type de données de référence et qu’une variable de ce type peut être nulle. Dans ce cas, il vaut mieux s'abstenir de telles opérations.

Integer myInteger1  = null;
Integer myInteger2 = myInteger1 + 5; 
Ici, nous aurons une exception :
Exception dans le thread "main" java.lang.NullPointerException"

Constantes de classe entière

La classe Integer fournit diverses constantes et méthodes pour travailler avec des entiers. Les voici:
  • SIZE signifie le nombre de bits dans le système numérique à deux chiffres occupés par le type int

  • BYTES est le nombre d'octets dans un système numérique à deux chiffres occupés par le type int

  • MAX_VALUE est la valeur maximale que le type int peut contenir

  • MIN_VALUE est la valeur minimale que le type int peut contenir

  • TYPE renvoie un objet de type Class de type int

Méthodes les plus utiles de la classe entière

Jetons maintenant un aperçu des méthodes les plus utilisées de la classe Integer . Les plus populaires d'entre elles, je suppose, sont les méthodes permettant de convertir un nombre à partir d'un String , ou vice versa.
  • static int parseInt(String s) cette méthode convertit String en int . Si la conversion n'est pas possible, NumberFormatException sera levée.

  • static int parseInt(String s, int radix) cette méthode convertit également le paramètre s en int . Le paramètre radix indique que le système numérique s a été initialement écrit.

En plus de parseInt , il existe également une méthode valueOf très similaire dans plusieurs variantes. Cependant, le résultat de valueOf sera Integer et parseInt sera int .
  • static Integer valueOf(int i) renvoie un Integer dont la valeur est i ;

  • static Integer valueOf(String s) fonctionne comme parseInt(String s) , mais le résultat sera Integer , pas int ;

  • static Integer valueOf(String s, int radix) fonctionne de la même manière que parseInt(String s, int radix) , mais le résultat est un Integer , pas un int .

Y a-t-il un problème avec la classe Integer ? Ah ouais, il y a…

Il existe donc deux types d'entiers (qui tiennent dans 32 bits) en Java : int et Integer . Pour comprendre les spécificités de chacun d'eux, nous devons connaître les éléments suivants sur le modèle de mémoire JVM : tout ce que vous déclarez est stocké soit dans la mémoire de pile (pile JVM spécifique à chaque thread), soit dans l'espace de tas. Les types primitifs ( int , long , float , boolean , double , char , byte , etc.) sont stockés dans la mémoire Stack. Tous les objets et tableaux sont stockés dans l'espace tas. Les références à ces objets et tableaux nécessaires aux méthodes sont stockées dans Stack. Donc. Pourquoi nous en soucions-nous ? Eh bien, vous voyez, Stack est plus petit que Heap (un inconvénient), mais il est beaucoup plus rapide d'allouer des valeurs dans Stack que dans Heap (un pro). Commençons par un type primitif int . Cela prend exactement 32 bits. C'est 32/8 = 4 octets. Parce que c'est un type primitif. Considérons maintenant Integer . C'est un objet, avec des frais généraux et des alignements supplémentaires. J'ai utilisé une bibliothèque jol pour mesurer sa taille :

public static void main(String[] args) {
 	System.out.println(ClassLayout.parseInstance(Integer.valueOf(1)).toPrintable());
}
et il s'est avéré qu'il occupait 16 octets :
Propriétés internes de l'objet java.lang.Integer : OFF SZ TYPE DESCRIPTION VALEUR 0 8 (en-tête de l'objet : marque) 0x000000748c90e301 (hachage : 0x748c90e3 ; âge : 0) 8 4 (en-tête de l'objet : classe) 0x000492a0 12 4 int Integer.value 1 Taille de l'instance : 16 octets
Quoi?! C'est 4 fois plus de mémoire ! Mais ne nous arrêtons pas là. En tant que développeurs Java, nous n'arrêtons normalement pas d'utiliser un seul entier. Ce que nous voulons vraiment, c'est en utiliser beaucoup. Comme dans une séquence. Par exemple, dans un tableau. Ou une liste. Les tableaux sont stockés dans le tas, tout comme les listes. Ainsi, l’attribution devrait prendre à peu près le même temps. Droite? Mais que se passe-t-il si nous devons allouer plus de mémoire ? Vérifions combien d'espace prend un tableau de 1 000 valeurs int primitives :

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());
}
Et le résultat est de 4016 octets :
OFF SZ TYPE DESCRIPTION VALEUR 0 8 (en-tête d'objet : marque) 0x0000000000000001 (non biaisable ; âge : 0) 8 4 (en-tête d'objet : classe) 0x00006c38 12 4 (longueur du tableau) 1000 12 4 (espace d'alignement/remplissage) 16 4000 int [I.<elements> N/A Taille de l'instance : 4 016 octets Pertes d'espace : 4 octets internes + 0 octets externes = 4 octets au total
OK, cela a du sens, étant donné qu'un seul int prend 4 octets. Qu'en est-il d'un ArrayList<Integer> de 1000 Integers ? Regardons:

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());
}
Et le résultat est de 20040 octets (encore une fois, 4 fois plus !) :
empreinte java.util.ArrayList@66d3c617d : COMPTE SOMME MOYENNE DESCRIPTION 1 4016 4016 [Ljava.lang.Object ; 1000 16 16000 java.lang.Integer 1 24 24 java.util.ArrayList 1002 20040 (total)
Ainsi, ArrayList<Integer> occupe 4 fois plus d'espace mémoire. Ce n'est pas bon. Mais quand même, les listes sont plus simples car nous pouvons ajouter et supprimer des éléments ! Oh Java… Pourquoi as-tu besoin de tout mettre en boîte ?! Mais j'oublie que Java est génial, et sa grandeur réside dans l'abondance de bibliothèques open source que nous pouvons utiliser ! Trove4j en fait partie. Il a TIntArrayList qui contient en interne des données int[] . Mesurons sa taille :

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());
}
Et le résultat est de 4040 octets (presque le même que simplement int[] !) :
gnu.trove.list.array.TIntArrayList@7440e464d empreinte : COUNT AVG SUM DESCRIPTION 1 4016 4016 [I 1 24 24 gnu.trove.list.array.TIntArrayList 2 4040 (total)
Au final, nous pouvons avoir le meilleur des deux mondes ! Des listes d'entiers qui prennent 4 fois moins de place. Et cela n'implique pas d'instances Integer . Seulement int s. Nous, les développeurs Java, nous soucions vraiment de la mémoire… Mais nous nous soucions également des performances. Il existe une merveilleuse bibliothèque de microbenchmarking avec un nom modeste jmh qui nous permet de mesurer les performances du code. Comparons d'abord les performances de calcul d'une somme de deux entiers aléatoires, encadrés ou non : La configuration de jmh est la suivante :

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
Les repères :

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;
}
Les résultats:
principal : test.SampleJavaBenchmark.testBoxedIntegersSum 5693337,344 ±(99,9%) 1198774,178 ops/s [Moyenne] (min, moy, max) = (1092314,989, 5693337,344, 12001683,428), stdev = 242158 3,144 IC (99,9 %) : [4494563,166, 6892111,522] (en supposant une distribution normale) principal : test.SampleJavaBenchmark.testPrimitiveIntegersSum 15295010,959 ± (99,9 %) 2555447,456 ops/s [Moyenne] (min, moy, max) = (4560097,059, 15295010,959, 24283809,447), stde v = 5162130,283 IC (99,9%) : [12739563.502, 17850458.415] (en supposant une distribution normale)
Ainsi, en moyenne, l'allocation et la somme des entiers primitifs sont plus de deux fois plus rapides que celles des entiers encadrés. Comparons maintenant les performances de création et de calcul de la somme des collections (ou des tableaux de 1 000 entiers) :

@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]
Les résultats : le tableau de primitives est plus de 4 fois plus rapide que le tableau de valeurs encadrées ( Integer s) ; presque six fois plus rapide que ArrayList de valeurs encadrées ( Integer s); et deux fois plus rapide qu'un TIntArrayList (qui décore en fait un tableau d'entiers primitifs). Par conséquent, si vous avez besoin d’une structure de données pour stocker une collection de valeurs entières et que sa taille ne va pas changer, utilisez un int[] ; si la taille doit changer, vous souhaiterez peut-être utiliser la bibliothèque tove4j avec TIntArrayList . Et voici la fin de mon essai où j'explique les inconvénients de l'utilisation du type Integer . Il existe des méthodes statiques intéressantes de Integer , dont je devrais parler avant de terminer. public static Integer getInteger(String nm, int val) ne fait pas ce que l'on pourrait penser, mais récupère une valeur Integer d'une propriété système. Val est la valeur par défaut au cas où cette propriété ne serait pas définie. public static String toBinaryString(int i) renvoie une chaîne avec une représentation binaire d'un nombre. Il existe des méthodes pour récupérer des représentations basées sur 16 ( toHexString ) et basées sur 8 ( toOctalString ). Il existe une méthode pour analyser une String en un int . Même si la chaîne est une représentation basée sur une base autre que 10. Voici quelques exemples : Integer.parseInt("-FF", 16) renvoie -255 Integer.parseInt("+42", 10) renvoie 42 Integer.parseInt("1100110", 2) renvoie 102
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION