55 votes

"==" en cas de concaténation de chaînes de caractères en Java

String a = "devender";
String b = "devender";
String c = "dev";
String d = "dev" + "ender";
String e = c + "ender";

System.out.println(a == b);     //case 1: o/p true

System.out.println(a == d);     //case 2: o/p true

System.out.println(a == e);     //case 3: o/p false

a & b les deux pointent vers le même String Literal dans le string constant pool. Donc true dans le cas 1

String d = "dev" + "ender";

devrait être utilisé en interne en utilisant quelque chose comme :

String d = new StringBuilder().append("dev").append("ender").toString();

Comment a & d pointent vers la même référence et non a & e ?

0 votes

0 votes

Le compilateur sait tout sur d au moment de la compilation, afin qu'il puisse être optimisé et interné. e utilise une variable, donc le compilateur ne sait pas tout à son sujet au moment de la compilation. De plus, aucun de ces comportements n'est garanti, même s'ils sont courants.

0 votes

@AzatNugusbayev J'ai suivi ce tutoriel :P

84voto

T.J. Crowder Points 285826

Quatre choses se passent :

  1. (Vous le savez déjà, mais pour les non-initiés) == teste pour voir si les variables pointent vers le même String objet pas équivalent des chaînes. Ainsi, même si x est "foo" y y est également "foo" , x == y peut être vrai ou faux, selon que x y y font référence au même String ou d'autres objets. C'est pourquoi nous utilisons equals pas == pour comparer des chaînes de caractères afin de déterminer leur équivalence. Tout ce qui suit n'a pour but que d'expliquer pourquoi == est parfois vrai, il ne s'agit pas d'une suggestion d'utiliser == pour comparer des chaînes de caractères :-)

  2. Les constantes de chaîne de caractères équivalentes (chaînes de caractères que le compilateur sait être des constantes selon diverses règles du JLS) au sein d'une même classe font référence à la même chaîne de caractères par le compilateur (qui les répertorie également dans l'en-tête de la classe). "piscine constante" ). C'est pourquoi a == b est vrai.

  3. Quand la classe est chargée, chacune de ses constantes de type chaîne est automatiquement interné  - le pool de chaînes de caractères de la JVM est vérifié pour trouver une chaîne de caractères équivalente et, si une chaîne de caractères est trouvée, cette chaîne de caractères est utilisée. String est utilisé (sinon, le nouvel objet String pour la nouvelle constante est ajouté au pool). Ainsi, même si x est une constante de type chaîne initialisée dans la classe Foo y y est une constante de type chaîne initialisée dans la classe Bar ils seront == l'un l'autre.

    Les points 2 et 3 ci-dessus sont couverts en partie par l'action de la Commission européenne. JLS§3.10.5 . (La partie concernant le pool de constantes de classe est un peu un détail d'implémentation, d'où le lien vers la spécification JVM plus haut ; le JLS parle simplement d'internat).

  4. Le compilateur effectue une concaténation de chaînes de caractères s'il s'agit de valeurs constantes, donc

    String d = "dev" + "ender";

    est compilé en

    String d = "devender";

    et "devender" est une constante de type chaîne de caractères à laquelle le compilateur et la JVM appliquent les points 2 et 3 ci-dessus. Par exemple, aucun StringBuilder est utilisé, la concaténation se fait au niveau de temps de compilation et non le temps d'exécution. Ce sujet est traité dans JLS§15.28 - Expressions constantes . Donc a == d est vrai pour la même raison a == b est vrai : ils font référence à la même chaîne de caractères constante, le compilateur s'est donc assuré qu'ils faisaient référence à la même chaîne de caractères dans le pool de constantes de la classe.

    Le compilateur ne peut pas le faire lorsque l'un des opérandes n'est pas une constante, donc il ne peut pas le faire avec :

    String e = c + "ender";

    ...même si l'analyse du code pourrait facilement montrer que la valeur de c sera définitivement "dev" et donc e sera définitivement "devender" . La spécification demande seulement au compilateur de faire la concaténation avec des valeurs constantes, spécifiquement. Donc, puisque le compilateur ne peut pas le faire, il produit la valeur StringBuilder auquel vous faites référence et que le travail est effectué au moment de l'exécution, en créant une nouvelle String objet. Cette chaîne n'est pas automatiquement internée, donc e finit par faire référence à un autre String que a le fait, et donc a == e est fausse.

    Notez que comme l'a dit Vinod si vous avez déclaré c como final :

    final String c = "dev";

    Alors ce serait un variable constante (oui, ils s'appellent vraiment comme ça) et donc le §15.28 s'appliquerait et le compilateur tournerait

    String e = c + "ender";

    en

    String e = "devender";

    et a == e serait également vrai.

Juste pour réitérer : Rien de tout cela ne signifie que nous devrions utiliser == pour comparer des chaînes de caractères pour l'équivalence :-) C'est ce que equals est pour.

18voto

TheLostMind Points 10813

Le compilateur fait beaucoup d'optimisation sous le capot.

String d = "dev" + "ender";

Ici, le compilateur remplacera "dev" + "ender" con "devender" lorsque le programme est en cours de compilation. Si vous ajoutez 2 littéraux (ceci s'applique aussi bien aux primitives qu'aux chaînes de caractères), le compilateur effectue cette optimisation.

Code Java :

String d = "dev" + "ender";

Code octet :

  0: ldc           #16                 // String devender

Venons-en à un cas particulier :

final String c = "dev"; // mark this as final
String e = c + "ender";

Réalisation de c final fera de la chaîne un constante de temps de compilation . Le compilateur se rendra compte que la valeur de c ne peut pas changer et remplacera donc toutes les occurrences de c avec la valeur "dev" lors de la compilation, donc e sera résolu pendant temps de compilation lui-même.

0 votes

"Le compilateur fait beaucoup d'optimisation sous le capot." Il ne le fait pas vraiment. Les compilateurs Java laissent cette tâche à la JVM. Le JLS exige que les compilateurs fassent ceci particulier remplacement uniquement car il s'agit d'une expression constante.

0 votes

@Boann - Le compilateur effectue ces optimisations sur la base de ce que le JLS spécifie. Je comprends, le compilateur laisse beaucoup de choses au JIT.

9voto

Comme vous l'avez dit en interne, la dernière concaténation est faite à quelque chose de similaire à

String e = new StringBuilder().append(c).append("ender").toString();

la mise en œuvre de toString() de StringBuilder crée une nouvelle chaîne . Voici l'implémentation.

public String toString() {
     // Create a copy, don't share the array
     return new String(value, 0, count);
}

Comparaison de chaînes de caractères à l'aide de == au lieu de .equals() renvoie à true seulement si les deux chaînes sont identiques . Dans ce cas elles ne sont pas les mêmes car la deuxième chaîne est créée comme une nouvelle objet de type String .

Les autres concaténations sont effectuées directement par le compilateur et aucune nouvelle chaîne n'est créée.

0 votes

Je pense que vous avez mal interprété la question ici. L'exemple que vous donnez renvoie bien true c'est les variables a y e qui ne le font pas.

1 votes

Oui, tu as raison, je l'ai changé. Merci

9voto

Bathsheba Points 23209

"dev" + "ender" est une expression constante évaluable au moment de la compilation : les deux arguments sont des chaînes de caractères. L'expression est donc "devender" .

On ne peut pas en dire autant de c + "ender" : certaines circonstances (du code s'exécutant sur un thread différent par exemple) pourraient conduire à des c étant fixé à une valeur différente. Qualification c como final élimine cette possibilité, et dans ce cas e ferait également référence au même objet que a .

Alors a , b y d font tous référence au même objet.

9voto

dasblinkenlight Points 264350

La différence entre d y e est que lorsque vous concaténer une chaîne de caractères littéraux la concaténation est effectuée au moment de la compilation. Le compilateur Java traite "dev" + "ender" de la même manière que "devender" produisant ainsi le même littéral au moment de la compilation. Puisque toutes les String sont internés, d qui est le résultat de "dev" + "ender" finit aussi par référencer le même objet que a y b 's "devender" .

L'expression pour e qui est c + "ender" est évalué à temps de fonctionnement . Même s'il produit la même chaîne de caractères, ce fait n'est pas utilisé par le compilateur. C'est pourquoi un autre String est produit, ce qui entraîne l'échec de la comparaison sur == .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X