web-dev-qa-db-fra.com

Comparaison d'une chaîne avec la chaîne vide (Java)

J'ai une question sur la comparaison d'une chaîne avec la chaîne vide en Java. Y a-t-il une différence, si je compare une chaîne avec la chaîne vide avec == ou equals? Par exemple:

String s1 = "hi";

if (s1 == "")

ou

if (s1.equals("")) 

Je sais qu'il faut comparer les chaînes (et les objets en général) avec equals, et non ==, mais je me demande si c'est important pour la chaîne vide.

32
user42155
s1 == ""

n'est pas fiable car il teste l'égalité de référence et non l'égalité d'objet (et String n'est pas strictement canonique).

s1.equals("")

est préférable mais peut souffrir d'exceptions de pointeur nul. Mieux encore:

"".equals(s1)

Aucune exception de pointeur nul.

EDIT: D'accord, le point a été posé sur forme canonique . Cet article le définit comme:

Supposons que nous ayons un ensemble S d'objets, avec une relation d'équivalence. Une forme canonique est donnée en désignant certains objets de S comme étant "sous forme canonique", de sorte que chaque objet considéré équivaut exactement à un objet sous forme canonique.

Pour vous donner un exemple pratique: prenez l'ensemble des nombres rationnels (ou les "fractions" sont communément appelées). Un nombre rationnel se compose d'un numérateur et d'un dénominateur (diviseur), qui sont tous deux des entiers. Ces nombres rationnels sont équivalents:

3/2, 6/4, 24/16

Les nombres rationnels sont généralement écrits de telle sorte que le pgcd (le plus grand diviseur commun) est 1. Ainsi, tous seront simplifiés en 3/2. 3/2 peut être considérée comme la forme canonique de cet ensemble de nombres rationnels.

Alors qu'est-ce que cela signifie en programmation lorsque le terme "forme canonique" est utilisé? Cela peut signifier plusieurs choses. Prenons par exemple cette classe imaginaire:

public class MyInt {
  private final int number;

  public MyInt(int number) { this.number = number; }
  public int hashCode() { return number; }
}

Le code de hachage de la classe MyInt est une forme canonique de cette classe car pour l'ensemble de toutes les instances de MyInt, vous pouvez prendre deux éléments m1 et m2 et ils obéiront à la relation suivante:

m1.equals(m2) == (m1.hashCode() == m2.hashCode())

Cette relation est l'essence de la forme canonique. Une façon plus courante de se produire est lorsque vous utilisez des méthodes d'usine sur des classes telles que:

public class MyClass {
  private MyClass() { }

  public MyClass getInstance(...) { ... }
}

Les instances ne peuvent pas être instanciées directement car le constructeur est privé. Ce n'est qu'une méthode d'usine. Ce qu'une méthode d'usine vous permet de faire, ce sont des choses comme:

  • Renvoie toujours la même instance (singleton abstrait);
  • Créez simplement une nouvelle entrée à chaque appel;
  • Renvoie les objets sous forme canonique (plus d'informations à ce sujet dans une seconde); ou
  • tout ce que tu aimes.

Fondamentalement, la méthode d'usine résume la création d'objets et personnellement, je pense que ce serait une fonctionnalité de langage intéressante pour forcer tous les constructeurs à être privés pour imposer l'utilisation de ce modèle, mais je m'égare.

Ce que vous pouvez faire avec cette méthode d'usine est de mettre en cache vos instances que vous créez de telle sorte que pour deux instances s1 et s2, elles obéissent au test suivant:

(s1 == s2) == s1.equals(s2)

Donc, quand je dis que String n'est pas strictement canonique, cela signifie que:

String s1 = "blah";
String s2 = "blah";
System.out.println(s1 == s2); // true

Mais comme d'autres l'ont fait, vous pouvez changer cela en utilisant:

String s3 = new String("blah");

et éventuellement:

String s4 = String.intern("blah");

Vous ne pouvez donc pas vous fier entièrement à l'égalité des références, vous ne devez donc pas vous y fier du tout.

Comme mise en garde contre le modèle ci-dessus, je dois souligner que le contrôle de la création d'objets avec des constructeurs privés et des méthodes d'usine ne garantit pas l'égalité de référence signifie l'égalité d'objet en raison de la sérialisation. La sérialisation contourne le mécanisme de création d'objet normal. Josh Bloch couvre ce sujet dans Effective Java (à l'origine dans la première édition quand il a parlé du modèle d'énumération typesafe qui est devenu plus tard une fonctionnalité de langage dans Java 5) et vous pouvez le contourner en surchargeant la méthode (private) readResolve (). Mais c'est délicat. Les chargeurs de classe affecteront également le problème.

Quoi qu'il en soit, c'est une forme canonique.

68
cletus

Cela dépendra si la chaîne est un littéral ou non. Si vous créez la chaîne avec

new String("")

Ensuite, il ne correspondra jamais à "" avec l'opérateur égal, comme indiqué ci-dessous:

    String one = "";
    String two = new String("");
    System.out.println("one == \"\": " + (one == ""));
    System.out.println("one.equals(\"\"): " + one.equals(""));
    System.out.println("two == \"\": " + (two == ""));
    System.out.println("two.equals(\"\"): " + two.equals(""));

-

one == "": true
one.equals(""): true
two == "": false
two.equals(""): true

Fondamentalement, vous voulez toujours utiliser equals ()

26
tddmonkey

C'est un peu en retrait par rapport à votre question d'origine, mais il y a toujours

if(s1.length() == 0)

Je crois que cela équivaut à la méthode isEmpty () de 1.6.

10
eaolson
"".equals(s)

Semble être la meilleure option, mais il y a aussi Stringutils.isEmpty(s) contenu dans la bibliothèque lang Apache commons

9
Raibaz

Réponse courte

s1 == ""         // No!
s1.equals("")    // Ok
s1.isEmpty()     // Ok: fast (from Java 1.6) 
"".equals(s1)    // Ok: null safe

Je voudrais assurer que s1 n'est pas nul et utiliser isEmpty ().

Remarque: la chaîne vide "" n'est pas une chaîne spéciale, mais compte comme toute autre "valeur".

Une réponse un peu plus longue

Les références aux objets String dépendent de la façon dont ils sont créés:

Les objets chaîne créés à l'aide de l'opérateur nouveau font toujours référence à des objets séparés, même s'ils stockent la même séquence de caractères:

String s1 = new String("");
String s2 = new String("");
s1 == s2 // false

Les objets String créés à l'aide de l'opérateur = suivi d'une valeur entre guillemets ( = "value" ) sont stockés dans un pool d'objets String: avant de créer un nouvel objet dans le pool, un objet avec la même valeur est recherché dans le pool et référencé s'il est trouvé.

String s1 = ""; // "" added to the pool
String s2 = ""; // found "" in the pool, s2 will reference the same object of s1
s1 == s2        // true

Il en va de même pour les chaînes créées contenant une valeur entre guillemets doubles ("valeur"), donc:

String s1 = "";  
s1 == "";        //true

La chaîne est égale à la méthode vérifie les deux, c'est pourquoi il est sûr d'écrire:

s1.equals("");

Cette expression peut lever une NullPointerException si s1 == null, donc, si vous ne vérifiez pas null avant, il est plus sûr d'écrire:

"".equals(s1);

Veuillez également lire Comment comparer les chaînes en Java?

J'espère que cela peut aider les utilisateurs moins expérimentés, qui peuvent trouver d'autres réponses un peu trop compliquées. :)

8
Linuslabo

Une chaîne, est une chaîne, est une chaîne, que ce soit la chaîne vide ou non. Utilisez equals().

6
Rob

Utilisez String.isEmpty () , ou StringUtils.isEmpty (String str) si vous avez besoin d'une vérification nulle.

3
matsev

Étant donné deux chaînes:

String s1 = "abc";
String s2 = "abc";

-ou -

String s1 = new String("abc");
String s2 = new String("abc");

L'opérateur == effectué sur deux objets vérifie l'identité de l'objet (il renvoie vrai si les deux opérateurs reviennent à la même instance d'objet.) Le comportement réel de == appliqué à Java.lang.Strings ne semble pas toujours cohérent en raison de Interning de chaîne.

En Java, les chaînes sont internées (au moins en partie à la discrétion de la JVM.) À tout moment, s1 et s2 peuvent ou non avoir été internées pour être la même référence d'objet (en supposant qu'elles ont la même valeur.) Ainsi s1 == s2 peut ou non renvoyer true, uniquement si s1 et s2 ont été internés.

Rendre s1 et s2 égaux à des chaînes vides n'a aucun effet sur cela - ils peuvent toujours ou non être internés.

En bref, == peut ou non retourner true si s1 et s2 ont le même contenu. s1.equals (s2) est garanti pour retourner vrai si s1 et s2 ont le même contenu.

0
Jared