OVH Cloud OVH Cloud

égalité en Java

15 réponses
Avatar
Julien Arlandis
J'ai une variable lue de type String
lorsque j'affiche :

System.out.println(lue.equals("QUIT"));
System.out.println(lue=="QUIT"));

Je n'ai pas forcement la même valeur de vérité. Quelle est donc la
différence entre la méthode equals() et l'opérateur == ?

10 réponses

1 2
Avatar
LR
J'ai une variable lue de type String
lorsque j'affiche :

System.out.println(lue.equals("QUIT"));
System.out.println(lue=="QUIT"));

Je n'ai pas forcement la même valeur de vérité. Quelle est donc la
différence entre la méthode equals() et l'opérateur == ?


L'opérateur == compare deux pointeurs.
La méthode equals() compare le contenu de deux objets.
A+Lilian

Avatar
Nicolas Delsaux
Le 29.01 2004, LR s'est levé et s'est dit : "tiens, si j'écrivais aux mecs
de fr.comp.lang.java ?"

L'opérateur == compare deux pointeurs.
La méthode equals() compare le contenu de deux objets.

C'est déja bien expliqué dans la javadoc (il me semble), et Lilian est

assez clair, mais j'en rajoute une couche conceptuelle :

equals est une égalité logique entre deux objets, c'est-à-dire qu'ils sont
indiscernables, comme le sont par exemple deux canettes de Coca.
== est une égalité physique : les deux références sont égales si elles
pointent vers le même endroit de la mémoire.




--
Nicolas Delsaux
FF>Et, croyez-moi, un dragon adulte qui se laisse aller, c'est pas de la
FF>crotte de bique, constipé et à pleine vitesse, on a déjà vu des morts.
K>On les a déjà sentis, surtout.
in fraf, explosion en plein vol

Avatar
vclassine
Julien Arlandis wrote in message news:<40185a50$0$29094$...
J'ai une variable lue de type String
lorsque j'affiche :

System.out.println(lue.equals("QUIT"));
System.out.println(lue=="QUIT"));

Je n'ai pas forcement la même valeur de vérité. Quelle est donc la
différence entre la méthode equals() et l'opérateur == ?


C'est tout a fait normal.

l'opérateur == compare les variables (ou expréssion) physiquement.
Chaque variable de type objet ou tableau est en fait une référence qui
donne "en gros" l'adresse du contenu de l'objet (un peu l'équivalent
d'un pointeur en C), l'opérateur == compare donc deux réferences.

Si on écrit:

Interger A = new Interger(12);
Interger B = A; //copie de la référence de A
Interger C = new Interger(A.intValue()); //création d'un nouvelle
objet et donc d'une nouvelle référence avec le même contenu que A.

Dans ce cas on a:

A==B
A!=C
B!=C

Par contre la méthode equals est une méthode laissé "redéfinissable"
par chaque auteur de classe qui, lorsque elle est redéfinie, compare
sémantiquement les objet, c'est a dire qu'elle en compare tout ou
partie du contenu.

La classe mère de tous les objets (Object) fournit une implémentation
par défaut de equals qui est en fait équivalente de l'opérateur "==",
ce qui est logique, vu que object n'a pas de contenu particulier elle
ne peut comparer que les références.

grosso modo l'implementation de equals dans object donne ça:

boolean equals(Object obj)
{
return (obj == this); //this est la référence de l'instance
courante.
}

Si les auteurs de la classe Interger n'avait pas redéfinie la méthode
equals ont obtiendrait:

A.equals(B) --> TRUE
A.equals(C) --> FALSE
B.equals(C) --> FALSE

C'est à dire la même chose qu'avec "==", mais ils ont redéfini la
méthode pour qu'elle compare le contenu des objets, ce qui doit donner
un truc equivalent de ça:

boolean equals(Object obj)
{
return (obj.intValue() == this.intValue()); //en réalité ils ne
passe certainement pas par intValue() puisqu'ils doivent avoir acces
directement à la valeurs. De plus ils doivent tester que obj est bien
de la classe Integer avant toute chose. Mais ça ne change rien au
résultat pour notre exemple...
}

Du coup on a:

A.equals(B) --> TRUE
A.equals(C) --> TRUE
B.equals(C) --> TRUE


Voila, bien venu à bord...

Avatar
Xavier Tarrago
Ajoutons qu'en ce qui concerne les String, il y a une petite astuce qui peut
faire gagner en performance si on fait beaucoup de comparaisons.
la méthode String.intern() retourne une String "canonique" de même contenu,
sachant que pour deux String de même contenu, c'est la même String qui est
retournée.
String str1 = "Hello";
String str2 = "Hello";
str1 == str2 // rapide, mais false
str1.equals(str2) // plus lent, mais true

String intstr1 = str1.intern();
String intstr2 = str2.intern();
intstr1 == intstr2 est vrai si et seulement si str2.equals(str1)

"Vincent" a écrit dans le message de
news:
Julien Arlandis wrote in message
news:<40185a50$0$29094$...

J'ai une variable lue de type String
lorsque j'affiche :

System.out.println(lue.equals("QUIT"));
System.out.println(lue=="QUIT"));

Je n'ai pas forcement la même valeur de vérité. Quelle est donc la
différence entre la méthode equals() et l'opérateur == ?


C'est tout a fait normal.

l'opérateur == compare les variables (ou expréssion) physiquement.
Chaque variable de type objet ou tableau est en fait une référence qui
donne "en gros" l'adresse du contenu de l'objet (un peu l'équivalent
d'un pointeur en C), l'opérateur == compare donc deux réferences.

Si on écrit:

Interger A = new Interger(12);
Interger B = A; //copie de la référence de A
Interger C = new Interger(A.intValue()); //création d'un nouvelle
objet et donc d'une nouvelle référence avec le même contenu que A.

Dans ce cas on a:

A==B
A!=C
B!=C

Par contre la méthode equals est une méthode laissé "redéfinissable"
par chaque auteur de classe qui, lorsque elle est redéfinie, compare
sémantiquement les objet, c'est a dire qu'elle en compare tout ou
partie du contenu.

La classe mère de tous les objets (Object) fournit une implémentation
par défaut de equals qui est en fait équivalente de l'opérateur "==",
ce qui est logique, vu que object n'a pas de contenu particulier elle
ne peut comparer que les références.

grosso modo l'implementation de equals dans object donne ça:

boolean equals(Object obj)
{
return (obj == this); //this est la référence de l'instance
courante.
}

Si les auteurs de la classe Interger n'avait pas redéfinie la méthode
equals ont obtiendrait:

A.equals(B) --> TRUE
A.equals(C) --> FALSE
B.equals(C) --> FALSE

C'est à dire la même chose qu'avec "==", mais ils ont redéfini la
méthode pour qu'elle compare le contenu des objets, ce qui doit donner
un truc equivalent de ça:

boolean equals(Object obj)
{
return (obj.intValue() == this.intValue()); //en réalité ils ne
passe certainement pas par intValue() puisqu'ils doivent avoir acces
directement à la valeurs. De plus ils doivent tester que obj est bien
de la classe Integer avant toute chose. Mais ça ne change rien au
résultat pour notre exemple...
}

Du coup on a:

A.equals(B) --> TRUE
A.equals(C) --> TRUE
B.equals(C) --> TRUE


Voila, bien venu à bord...



Avatar
TestMan
Désolé mais ya une petite erreur, dans ton cas str1 == str2 est vrai ;-)

Un petit test que tout le monde peut compiler :

public class Equals{

public static void main(String[] args){
String str1 = "toto";
String str2 = "toto";
String str3 = new String("toto");
String str4 = new String("toto").intern();

System.out.println("1==2?"+ (str1==str2)); // Vrai !
System.out.println("1==3?"+ (str1==str3)); // Faux !
System.out.println("1==4?"+ (str1==str4)); // Vrai !

}

}


Dans le cas str1 et str2, cette déclaration référence la chaine dans le
pool de constante de contenu "toto". Il faut noter que si une chaine
existe déjà dans le pool la VM lui assigne la référence plutôt que d'en
créer une autre.

Ainsi le test 1==2 est-il vrai car les objets ont les même références.

Pour ce qui est de str3, on crée ici un objet traditionel (donc hors du
pool de constante) et qui n'a dont pas de raison d'être le même que str1
ou str2. Ce qui explique que 1==3 est faux.

Pour ce qui est du dernier, à partir d'un objet chaine traditionel, la
méthode .intern() va piocher dans le pool de constante pour retourner un
objet identique si il existe, sinon la VM l'ajoute au pool. str1 ayant
ajouté cette valeur au pool, c'est la même référence qui est retournée.
Voici donc expliqué pourquoi 1==4 est vrai.

Pour finir, on rappelera qu'avec .equals() (équivalence) tous les tests
croisés sont vrai.

A+

TM

Xavier Tarrago wrote:
Ajoutons qu'en ce qui concerne les String, il y a une petite astuce qui peut
faire gagner en performance si on fait beaucoup de comparaisons.
la méthode String.intern() retourne une String "canonique" de même contenu,
sachant que pour deux String de même contenu, c'est la même String qui est
retournée.
String str1 = "Hello";
String str2 = "Hello";
str1 == str2 // rapide, mais false
str1.equals(str2) // plus lent, mais true

String intstr1 = str1.intern();
String intstr2 = str2.intern();
intstr1 == intstr2 est vrai si et seulement si str2.equals(str1)



Avatar
vclassine
"Xavier Tarrago" wrote in message news:<bvd52r$3m1$...
Ajoutons qu'en ce qui concerne les String, il y a une petite astuce qui peut
faire gagner en performance si on fait beaucoup de comparaisons.
la méthode String.intern() retourne une String "canonique" de même contenu,
sachant que pour deux String de même contenu, c'est la même String qui est
retournée.
String str1 = "Hello";
String str2 = "Hello";
str1 == str2 // rapide, mais false
str1.equals(str2) // plus lent, mais true

String intstr1 = str1.intern();
String intstr2 = str2.intern();
intstr1 == intstr2 est vrai si et seulement si str2.equals(str1)


Comme ça en réfléchissant 5 minutes je me dit que c'est de
l'optimisation fine qui ne doit pas s'utiliser n'importe comment et
qui ne doit être que rarement utilisée.

Concrètement je doute que faire:

strA.intern() == strB.intern()

soit, a priori, plus rapide que:

strA.equals(strB);


Ont peut toujours utiliser l'opérateur == dès l'instant où on peut
garantir que tous les String identique ont la même ref et que deux
string ayant la même ref sont identique.

La deuxième partie est évidente. La première partie peut nécéssiter
l'utilisation de la intern() mais pas toujours. Après tout si on sait
que les String en question sont produit par un même lot de String
litérrales, ça ne sert à rien de jouer avec intern (sauf si on s'amuse
à écrire des conneries du style strA = new String(strB)).

Comme ça je dirais que ça peut être utile si on lit (BBD, fichiers,
I/O utilisateurs...) pas mal de String identiques qu'on va comparer
souvent. Si c'est le cas il faut se demander si ces Strings ne sont
pas issue d'une liste finie de valeurs. Si c'est le cas, pourquoi ne
pas utiliser un stockage (voire dans le code) une représentation plus
légère ou moins traitre que les String: entier, pattern "typesafe
enum"...

Bref à mon avis la conception du programme de manière à éviter le cas
d'utilisation de la méthode intern est très souvent plus efficace que
l'utilisation de cette méthode...

Mais bon je peux me gourrer je n'y ais pas réfléchit longtemps...

Plus généralement utiliser "==" à la place de equals peut être très
dangereux. A la moindre inatention dans l'affectation des String, ont
se retrouve avec un test d'égalité qui ne fait pas se qu'on attend de
lui, et ce genre de truc est très pénible à chercher parce que ça
n'explose pas...

A utiliser avec parcimonie, selon moi...

Avatar
Julien Arlandis
Désolé mais ya une petite erreur, dans ton cas str1 == str2 est vrai ;-)

Un petit test que tout le monde peut compiler :

public class Equals{

public static void main(String[] args){
String str1 = "toto";
String str2 = "toto";
String str3 = new String("toto");
String str4 = new String("toto").intern();

System.out.println("1==2?"+ (str1==str2)); // Vrai !
System.out.println("1==3?"+ (str1==str3)); // Faux !
System.out.println("1==4?"+ (str1==str4)); // Vrai !

}

}



Bon, j'avoue ne pas avoir tout compris sur le coup. Quelle différence entre
String str1 = "toto";
et
String str3 = new String("toto");

La seconde forme me parait plus logique, dans le premier cas l'objet
n'est pas instancié, comment est ce possible?

Avatar
jz
Julien Arlandis wrote:

Bon, j'avoue ne pas avoir tout compris sur le coup. Quelle différence entre
String str1 = "toto";
et
String str3 = new String("toto");

La seconde forme me parait plus logique, dans le premier cas l'objet
n'est pas instancié, comment est ce possible?



Avec une String les 2 écritures sont correctes et équivalentes. L'objet
est donc instancié dans les 2 cas.

Jacques

Avatar
vclassine
Julien Arlandis wrote in message news:<401a8852$0$1165$...
Désolé mais ya une petite erreur, dans ton cas str1 == str2 est vrai ;-)

Un petit test que tout le monde peut compiler :

public class Equals{

public static void main(String[] args){
String str1 = "toto";
String str2 = "toto";
String str3 = new String("toto");
String str4 = new String("toto").intern();

System.out.println("1==2?"+ (str1==str2)); // Vrai !
System.out.println("1==3?"+ (str1==str3)); // Faux !
System.out.println("1==4?"+ (str1==str4)); // Vrai !

}

}



Bon, j'avoue ne pas avoir tout compris sur le coup. Quelle différence entre
String str1 = "toto";
et
String str3 = new String("toto");


ça y est on rentre dans les débats "d'experts" - qui ne sont pas
réservé aux expert: la preuve, j'y suis :-) - et du coup on embrouille
celui qui posait une question simple.

Bon, en gros:

String str1 = "toto";
//Là tu dis à la VM: "mets moi une String contenant "toto" dans str1".

String str3 = new String("toto");
//Là tu dis à la VM: "mets moi une NOUVELLE String contenant "toto"
dans str1".

Les deux chaines ont le même contenu, mais on une référence
différente.

La seconde forme me parait plus logique, dans le premier cas l'objet
n'est pas instancié, comment est ce possible?


Pour expliquer, reprenons les 3 lignes:

//on suppose que c'est les seuls instructions chargée
String str1 = "toto";
//la vm cherche "toto" dans sa table de chaine litéralles, ne la
trouve pas, la crée, et enfin affecte sa référence str1;

String str2 = "toto";
//la vm cherche "toto" dans sa table de chaine litéralles, la trouve
et affecte sa référence à str2;

String str3 = new String("toto");
//la vm cherche "toto" dans sa table de chaine litéralles, la trouve,
la copie dans une nouvelle instance de string et affecte la référence
de cette nouvelle instance à str3;

On constate que cette écriture entraine un code moins rapide, la seule
chose qu'elle fait en plus c'est créer une nouvelle référence ce qui
est inintéressant puisque les Strings sont des objets immuables (une
fois crée il ne change pas).

Au passage, l'immuabilité des String perturbe souvent les débutants.

Par exemple si tu fais

str1.toUpper();

tu ne fais rien d'utile, car en fait la méthode toUpper() ne touche
pas au contenu de str1, mais elle crée une nouvelle string (donc
nouvelle ref) contenant le contenu, convertit en majuscule, de str1.
Si tu ne stockes pas cette nouvelle ref, tu as appelé la méthode pour
rien...

Rassures-toi, la classe String est surement la plus perturbante de
toutes ;-)


Avatar
Julien Arlandis
tu ne fais rien d'utile, car en fait la méthode toUpper() ne touche
pas au contenu de str1, mais elle crée une nouvelle string (donc
nouvelle ref) contenant le contenu, convertit en majuscule, de str1.
Si tu ne stockes pas cette nouvelle ref, tu as appelé la méthode pour
rien...

Rassures-toi, la classe String est surement la plus perturbante de
toutes ;-)


Merci, cette fois j'ai compris.

Les expressions régulières comment ça marche en java concrètement?
Je voudrais backslasher mes chaînes de caractères avant de les injecter
dans les requêtes SQL.
Je fais lu = lu.replaceAll("[']","'");
Mais le contenu de lu n'a pas changé!

1 2