2009-07-10 16 views
30

EDIT Gracias por la pronta respuesta. Por favor mira cuál es la verdadera pregunta. Lo hice audaz esta vez.Cuando "" == s es falso pero "" .equals (s) es verdadero

Entiendo la diferencia entre == y .equals. Por lo tanto, no es mi pregunta (que en realidad añadido un poco de contexto para que)


que estoy realizando la validación abajo para cadenas vacías:

if("" == value) { 
    // is empty string 
} 

En los últimos valores cuando ir a buscar desde el db o deserializar objetos de otro nodo, esta prueba falló, porque las dos instancias de cadena eran de hecho referencias de objetos diferentes, aunque contenían los mismos datos.

Así que la solución para esas situaciones era

if("".equals(value)) { 
    // which returns true for all the empty strings 
} 

estoy bien con eso. Eso está claramente entendido.

Hoy en día esto ocurrió una vez más, pero me desconcertó porque la aplicación de este tiempo es una muy pequeña aplicación independiente que no utiliza red en todo, así que no hay nueva cadena se obtiene de la base de datos ni de deserizalized otro nodo

Así que la pregunta es:


Bajo qué circunstancias OTROS:

"" == value // yields false 

y

"".equals(value) // yields true 

Para una aplicación independiente local?

Estoy bastante seguro de que nuevo String() no se está utilizando en el código.

Y la única forma en que una cadena de referencia podría ser "" se debe a que se está asignando "" directamente en el código (o eso es lo que pensaba) como en:

String a = ""; 
String b = a; 

assert "" == b ; // this is true 

De alguna manera (después de leer el código más tengo una pista fueron creados) dos referencias a objetos cadena vacía diferentes, me gustaría saber cómo

más en la línea de respuesta jjnguys:

Byte!

EDIT: Conclusión

he encontrado la razón.

Después de la sugerencia de jjnguy pude ver con otros ojos el código.

El método culpable: StringBuilder.toString()

A new String object is allocated and initialized to contain the character sequence currently represented by this object.

Doh ...

StringBuilder b = new StringBuilder("h"); 
    b.deleteCharAt(0); 
    System.out.println("" == b.toString()); // prints false 

Misterio resuelto.

El código utiliza StringBuilder para tratar con una cadena cada vez mayor. Resulta que en algún momento alguien lo hizo:

public void someAction(String string) { 
     if("" == string) { 
      return; 
     } 

     deleteBankAccount(string); 
} 

y utilizar

someAction(myBuilder.toString()); // bug introduced. 

P. S. ¿He leído demasiado CodingHorror últimamente? ¿O por qué siento la necesidad de agregar algunas imágenes divertidas de animales aquí?

+4

if (value! = Null && value.length == 0) {...}, y debe usar equals porque == para objetos compara referencias, es decir, son el mismo objeto. Mientras que en tiempo de compilación Java encuentra cadenas idénticas y les hace compartir la misma referencia (las cadenas son inmutables), en tiempo de ejecución es fácil crear cadenas vacías que tienen referencias diferentes. – JeeBee

+0

Como mi respuesta sugiere ... olvida lo que "pensaste". Si quiere comprobar si algo tiene el mismo valor que la cadena vacía, utilice iguales. Estás tratando de depender del compilador que intercede en tus cadenas, lo cual Josh Bloch señala que no es una herramienta para programadores :-). Vea abajo. – Tom

+0

Varias respuestas ahora muestran manipulaciones de cadena simples que producen diferentes objetos String de longitud cero. Las redes y la bondad no tienen nada que ver con esas respuestas. – djna

Respuesta

24
String s = ""; 
String s2 = someUserInputVariale.toLowercase(); // where the user entered in "" 

Algo así causaría que s == s2 se evalúe como falso.

Gran cantidad de código alféizar crear nuevo Strings sin exponer la llamada a new String().

+0

Cada vez que se genera/crea/manipula una cadena en tiempo de ejecución, tiene el potencial de suceder. Supongo que la VM puede crear una nueva Cadena para el grupo de cadenas como lo considere adecuado, aunque para la optimización, sería mejor usar una Cadena existente. – user101884

+0

Ortografía es para diccionarios ... – jjnguy

+12

No, me gusta. "Nosotros los programadores de Java ni siquiera sabemos cómo HABLAR optimyz ... optimo ... mira, el JIT lo hace por nosotros ¿de acuerdo?" –

2

Deberías intentar considerar String.length() == 0.

+0

Sugeriría esto también, a menos que la cadena sea nula. ;) –

+0

Esto no responde a su pregunta ... – jjnguy

7

¿Podría esperar que "abcde".substring(1,2) y "zbcdefgh".substring(1,2) produzcan el mismo objeto String?

Ambos producen subcadenas "iguales" extraídas de dos cadenas diferentes, pero parece bastante razonable que sean objetos diferentes, por lo que == los ve como diferentes.

Considere ahora cuando la subcadena tiene longitud 0, substring(1, 1). Produce una cadena de longitud cero, pero no es sorprendente que "abcde".substring(1,1) sea un objeto diferente de "zbcdefgh".substring(1,2) y, por lo tanto, al menos uno de ellos es un objeto diferente de "".

+1

No creo que esta analogía realmente aclare nada –

+2

Sí. Dice que incluso si no hay llamadas explícitas a "new String", una gran cantidad de código aún crea nuevas cadenas ... subcadena, toUpperCase, etc. – erickson

+0

Creo que esta es una buena respuesta. claramente uno esperaría que == arroja valores falsos, pero los iguales arrojan valores verdaderos. Y eso es lo que el interrogador preguntó realmente, no "cuál es la diferencia entre == y es igual a" lo que la mayoría le respondió –

0

Por qué no usar:

if (value != null && value.length == 0) { 
    // do stuff (above could be "== null ||" 
} 

Debe utilizar equals() porque == para objetos compara referencias, es decir, ¿son el mismo objeto. Mientras que en tiempo de compilación Java encuentra cadenas idénticas y las hace compartir la misma referencia (las cadenas son inmutables), en tiempo de ejecución es fácil crear cadenas vacías que tienen referencias diferentes, donde == falla para su intención típica de equals().

3

Como lo entiendo mientras compilo el código de Java para bytecode o mientras ejecuto el programa, las mismas cadenas serán referenciadas al mismo objeto en la mayoría de los casos para ahorrar memoria. Entonces a veces te salgas con la == comparaciones de cadenas. Pero esta es una optimización del compilador en la que no puede confiar.

Pero a veces sucede que el compilador decide no hacer esta optimización o no hay forma de que el programa vea que las cadenas son las mismas y, de repente, falla la comprobación, ya que se basa en una optimización subyacente vudú que depende de la implementación de la jvm que está usando, etc.

Por lo tanto, siempre es bueno usar iguales. Para cadenas vacías hay otras posibilidades como comparar con longitud == 0 o si no te importa la compatibilidad hacia atrás hay string.empty().

10

Si puede agarrar una suspensión del libro Java Puzzlers de Joshua Bloch y Neal Gafter, y mirar rompecabezas 13, "Animal Farm" ... tiene un consejo sobre este tema. Voy a copiar un texto relevante:

"Usted puede ser consciente de que las constantes de tiempo de compilación de tipo String son internados [JLS 15.28] En otras palabras, cualquiera de los dos expresiones constantes de tipo String que designan al. la misma secuencia de caracteres está representada por referencias de objeto idénticas ... Su código rara vez, o nunca, depende de la interna de constantes de cadena El internamiento se diseñó únicamente para reducir la huella de memoria de la máquina virtual, no como una herramienta para programadores ... Al comparar referencias de objetos, debe usar el método equals con preferencia al operador == a menos que necesite comparar la identidad del objeto en lugar del valor ".

Esa es la referencia anterior que mencioné ... páginas 30 - 31 en mi libro.

+2

En otras palabras ... use iguales porque es lo que quiere decir ... y es a lo que se refiere. – Tom

20
"" == value // yields false 

y

"".equals(value) // yields true 

ningún momento el valor de la variable value no ha sido internado. Este será el caso si el valor se calcula en tiempo de ejecución. Ver el JLS section 3.10.5 String Literals de código de ejemplo que ilustra esto:

Thus, the test program consisting of the compilation unit (§7.3):

package testPackage; 
class Test { 
    public static void main(String[] args) { 
     String hello = "Hello", lo = "lo"; 
     System.out.print((hello == "Hello") + " "); 
     System.out.print((Other.hello == hello) + " "); 
     System.out.print((other.Other.hello == hello) + " "); 
     System.out.print((hello == ("Hel"+"lo")) + " "); 
     System.out.print((hello == ("Hel"+lo)) + " "); 
     System.out.println(hello == ("Hel"+lo).intern()); 
    } 
} 
class Other { static String hello = "Hello"; } 

and the compilation unit:

package other; 
public class Other { static String hello = "Hello"; } 

produces the output:

true true true true false true 

This example illustrates six points:

  • Literal strings within the same class (§8) in the same package (§7) represent references to the same String object (§4.3.1).
  • Literal strings within different classes in the same package represent references to the same String object.
  • Literal strings within different classes in different packages likewise represent references to the same String object.
  • Strings computed by constant expressions (§15.28) are computed at compile time and then treated as if they were literals.
  • Strings computed at run time are newly created and therefore distinct.
  • The result of explicitly interning a computed string is the same string as any pre-existing literal string with the same contents.
0

El javadoc para String.intern() tiene un buen comentario de == vs .equals().

La documentación también aclara que cada literal de cadena es intern 'd.

public String intern()

Returns a canonical representation for the string object.

A pool of strings, initially empty, is maintained privately by the class String.

When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned.

It follows that for any two strings s and t, s.intern() == t.intern() is true if and only if s.equals(t) is true.

All literal strings and string-valued constant expressions are interned. String literals are defined in §3.10.5 of the Java Language Specification

Returns: a string that has the same contents as this string, but is guaranteed to be from a pool of unique strings.

0

Si utiliza Google búsqueda de código, se puede encontrar un montón de lugares donde las personas hacen de este mismo error: google for file:.java \=\=\ \"\" Por supuesto, esto puede ser un lenguaje correcto en circunstancias controladas cuidadosamente, pero por lo general, es sólo un error.

+0

Sí. Bueno, para las aplicaciones que usan la red es bastante común que falle, pero para el código autónomo es muy extraño. ¡La consulta de google es agradable! :) – OscarRyz

+0

Lamentablemente, el enlace está muerto – ComputerDruid

Cuestiones relacionadas