web-dev-qa-db-fra.com

Pourquoi le comportement du pool de constantes entières change-t-il à 127?

Je ne suis pas en mesure de comprendre comment fonctionne le Java Constant Pool for Integer).

Je comprends le comportement de Strings, et donc je peux me justifier que c'est également le cas avec les constantes entières.

Donc, pour les entiers

Integer i1 = 127;
Integer i2 = 127;
System.out.println(i1==i2); // True

&

Integer i1 = new Integer(127);
Integer i2 = new Integer(127);
System.out.println(i1==i2); // False

Jusqu'ici tout se passe dans ma tête.

Ce que je ne peux pas digérer, c'est qu'il se comporte différemment lorsque j'augmente l'entier à partir de 127. Ce comportement change après 127, ci-dessous est l'extrait de code

Integer i1 = 128;
Integer i2 = 128;
System.out.println(i1==i2); // False. WHY?????

Quelqu'un peut-il m'aider à comprendre cela?

40
Vivek

Non, le pool constant de nombres ne fonctionne pas de la même manière que pour les chaînes. Pour les chaînes, seules les constantes au moment de la compilation sont internées - alors que pour les types d'encapsuleur pour les types entiers, toute opération de boxe utilisera toujours le pool s'il est applicable pour cette valeur. Ainsi, par exemple:

int x = 10;
int y = x + 1;
Integer z = y; // Not a compile-time constant!
Integer constant = 11;
System.out.println(z == constant); // true; reference comparison

Le JLS garantit une petite plage de valeurs regroupées, mais les implémentations peuvent utiliser une plage plus large si elles le souhaitent.

Notez que bien que ce ne soit pas garanti, chaque implémentation que j'ai examinée utilise Integer.valueOf pour effectuer des opérations de boxe - afin que vous puissiez obtenir le même effet sans l'aide de la langue:

Integer x = Integer.valueOf(100);
Integer y = Integer.valueOf(100);
System.out.println(x == y); // true

De section 5.1.7 du JLS :

Si la valeur p encadrée est vraie, fausse, un octet ou un caractère compris entre\u0000 et\u007f, ou un nombre entier ou court compris entre -128 et 127 (inclus), alors que r1 et r2 soient les résultats de deux conversions de boxe de p. Il est toujours vrai que r1 == r2.

Idéalement, encadrer une valeur primitive donnée p donnerait toujours une référence identique. En pratique, cela peut ne pas être possible en utilisant les techniques de mise en œuvre existantes. Les règles ci-dessus sont un compromis pragmatique. La dernière clause ci-dessus exige que certaines valeurs communes soient toujours encadrées dans des objets indiscernables. L'implémentation peut les mettre en cache, paresseusement ou avec empressement. Pour les autres valeurs, cette formulation interdit toute hypothèse sur l'identité des valeurs encadrées de la part du programmeur. Cela permettrait (mais n'exigerait pas) le partage de tout ou partie de ces références.

Cela garantit que dans la plupart des cas, le comportement sera celui souhaité, sans imposer de pénalité de performance indue, en particulier sur les petits appareils. Des implémentations moins limitées en mémoire peuvent, par exemple, mettre en cache toutes les valeurs char et short, ainsi que les valeurs int et long dans la plage de -32K à + 32K.

45
Jon Skeet

Java maintient le pool entier de -128 À 127

Déclarer un entier comme ci-dessous

Integer i1 = 127;

Résultats à

Integer i1 = Integer.valueOf(127);

Donc, ce qui se passe réellement pour le premier cas est

Integer i1 = 127;<---Integer.valueOf(127);
Integer i2 = 127;<---Integer.valueOf(127);<---Same reference as first

Du code source de Integer pour la méthode classe valueOf

public static Integer valueOf(int i) {
    if(i >= -128 && i <= IntegerCache.high)
        return IntegerCache.cache[i + 128];
    else
        return new Integer(i);
}

Ainsi, vous obtenez la même référence si la valeur est comprise entre -128 À 127 Et vous appelez valueOf sinon il retourne simplement new Integer(i)

Et comme la référence est la même, votre opérateur == Fonctionne pour les entiers renvoyés par valueOf entre cette plage.

22
Amit Deshpande

Java met en cache les objets entiers dans la plage -128 to 127. Ainsi, lorsque vous essayez d'affecter une valeur dans cette plage à un objet wrapper, l'opération boxing appellera Integer.valueOf et à son tour, il assignera une référence à l'objet déjà dans le pool.

En revanche, si vous affectez une valeur en dehors de cette plage à un type de référence wrapper, Integer.valueOf créera un nouvel objet Integer pour cette valeur. Et par conséquent, la comparaison des objets reference pour Integer ayant une valeur en dehors de cette plage vous donnera false

Alors,

Integer i = 127;  --> // Equivalent to `Integer.valueOf(127)`
Integer i2 = 127;

// Equivalent to `Integer.valueOf(128)`
// returns `new Integer(128)` for value outside the `Range - [-128, 127]`
Integer i3 = 128; 
Integer i4 = 128;

System.out.println(i == i2); // true, reference pointing to same literal
System.out.println(i3 == i4); // false, reference pointing to different objects

Mais, lorsque vous créez vos instances entières à l'aide de l'opérateur new, un nouvel objet sera créé sur le tas. Alors,

Integer i = new Integer(127);
Integer i2 = new Integer(127);

System.out.println(i == i2); // false
8
Rohit Jain

Dans les versions plus récentes de Java cache Integer est compris entre -128 et 127 (256 valeurs). Regardez ici

Que fait exactement la comparaison de nombres entiers avec ==?

2
suraj_fale