Java Language Piège - La surutilisation des types d’emballages primitifs est inefficace


Exemple

Considérez ces deux morceaux de code:

int a = 1000;
int b = a + 1;

et

Integer a = 1000;
Integer b = a + 1;

Question: Quelle version est la plus efficace?

Réponse: Les deux versions sont presque identiques, mais la première version est beaucoup plus efficace que la deuxième.

La deuxième version utilise une représentation des nombres qui utilise plus d'espace, et s'appuie sur la mise en boîte automatique et le désencapsulation automatique en arrière-plan. En fait, la deuxième version est directement équivalente au code suivant:

Integer a = Integer.valueOf(1000);               // box 1000
Integer b = Integer.valueOf(a.intValue() + 1);   // unbox 1000, add 1, box 1001

En comparant ceci à l'autre version qui utilise int , il y a clairement trois appels de méthode supplémentaires quand Integer est utilisé. Dans le cas de valueOf , les appels vont chacun créer et initialiser un nouvel objet Integer . Tout ce travail supplémentaire de boxe et de déballage va probablement rendre la deuxième version plus lente que la première.

En plus de cela, la deuxième version alloue des objets sur le tas dans chaque appel valueOf . Bien que l'utilisation de l'espace soit spécifique à la plate-forme, il est probable qu'il soit de l'ordre de 16 octets pour chaque objet Integer . En revanche, la version int nécessite un espace de pile supplémentaire, en supposant que a et b sont des variables locales.


Une autre grande raison pour laquelle les primitives sont plus rapides que leur équivalent en boîte est la manière dont leurs types de tableau respectifs sont disposés en mémoire.

Si vous prenez int[] et Integer[] comme exemple, dans le cas d'un int[] les valeurs int sont contiguës en mémoire. Mais dans le cas d'un Integer[] ce ne sont pas les valeurs qui sont mises en page, mais les références (pointeurs) aux objets Integer , qui contiennent à leur tour les valeurs int réelles.

En plus d'être un niveau supplémentaire d'indirection, il peut s'agir d'un gros réservoir lorsqu'il s'agit de mettre en cache une localité lors d'une itération sur les valeurs. Dans le cas d'un int[] le processeur peut récupérer toutes les valeurs du tableau, dans son cache, car elles sont contiguës en mémoire. Mais dans le cas d'un Integer[] le processeur doit éventuellement effectuer une extraction de mémoire supplémentaire pour chaque élément, car le tableau contient uniquement des références aux valeurs réelles.


En bref, l'utilisation de types d'encapsuleurs primitifs est relativement coûteuse à la fois en termes de ressources processeur et mémoire. Les utiliser inutilement est efficace.