90

Estoy creando un mini juego en el que el usuario intenta adivinar un nombre. Pero cuando quiero comparar dos cadenas de texto para ver si son iguales no parece funcionar.

final String miNombre = "Jordi";
Scanner input = new Scanner(System.in);
System.out.println("Adivina mi nombre: ");

while (true) {
    String intento = input.next();
    if(intento == miNombre) {
        System.out.println("Acertaste!");
        break;
    } else {
        System.out.println("Intentalo de nuevo!");
    }
}

SALIDA:

Adivina mi nombre: 
manuel
Intentalo de nuevo!
jordi
Intentalo de nuevo!
Jordi
Intentalo de nuevo!
Jordi Castilla
  • 7,189
  • 10
  • 35
  • 60

12 Answers12

116

En Java solo los tipos primitivos (Descritos en el JLS (§4.2), por ejemplo int o char) se comparan con ==, los Strings (y los demas objetos) en Java se comparan entre ellos con el metodo equals.

String#equals(Object)

Compara este String con el objeto especificado. El resultado es true si, y solo si el argumento no es null y es un objeto del tipo String que representa la misma secuencia de caracteres que este objecto.


  • JLS (§15.21.3) Si bien puede utilizarse == para comparar referencias de tipo String, una prueba de igualdad determina si los dos operandos refieren al mismo objeto String. El resultado es false si los operandos son distintos objetos String, incluso si contienen la misma secuencia de caracteres.

Por lo tanto tu comparacion debe ser:

if(miNombre.equals(intento)) {

SALIDA:

Adivina mi nombre: 
manuel
Intentalo de nuevo!
Jordi
Acertaste!

ACLARACIONES:

  • He puesto la variable miNombre al principio de la comparación para evitar una NullPointerException si intento == null (si, las comparaciones con null tambien se hacen con ==).

    Como extra: si null es un valor válido en la representación (y por tanto queremos evitar una NPE), Objects.equals está disponible a partir de Java 7 y se encarga de devolver true si ambas son null o false si sólo lo es una.

  • En este caso se puede usar el metodo String#equalsIgnoreCase(Object) que ignorara MAYUSCULAS o MINUSCULAS al hacer la comparacion:

    if(miNombre.equalsIgnoreCase(intento)) {       
    
  • JLS (§15.21) Los operadores de igualdad (== y !=) se pueden utilizar para comparar dos operandos que son convertibles (§5.1.8) de tipo numérico, o los dos operandos de tipo boolean o Boolean o dos operandos que > son de tipo de referencia o el tipo null. Todos los demás casos causan un error en tiempo de compilación.

Jordi Castilla
  • 7,189
  • 10
  • 35
  • 60
  • 1
    Como extra: si `null` es un valor válido en la representación (y por tanto queremos evitar una NPE), `Objects.equals` está disponible a partir de Java 7 y se encarga de devolver `true` si ambas son `null` o `false` si sólo lo es una. – Darkhogg Dec 02 '15 at 17:31
  • Extra: utiliza `intento.intern() == miNombre.intern()` en lugar de `equals` :P –  Feb 25 '16 at 04:01
34

Desde Java 7 es posible comparar la igualdad de dos strings utilizando el método equals de la clase java.util.Objects. Esto es:

String a = ...
String b = ...

if (Objects.equals(a, b)) {
    // iguales
} else {
    // diferentes
}

Puesto que a.equals(b) o b.equals(a) pueden lanzar java.lang.NullPointerException si a es null o b es null respectivamente, al utilizar este camino, a o b pueden ser null sin ningún riesgo.

Si estás utilizando Java 6 (o menor), a partir del código de esta clase, podemos reescribir nuestro if de la siguiente manera:

if (a == b || (a != null && a.equals(b))) {
    // iguales
} else {
    // diferentes
}
Paul Vargas
  • 181
  • 1
  • 20
  • 39
27
  • El operador "==" compara dos referencias, es decir, devuelve true si ambos objetos ocupan la misma posición de memoria.

  • El método "equals" compara los valores de los objetos.

Jorgesys
  • 103,630
  • 13
  • 52
  • 124
Jose4Linux
  • 371
  • 2
  • 4
18

En el lenguaje Java se útiliza la función equals() para comparar strings, NUNCA debería usarse el operador == .

String nombre = "elenasys";

if (nombre.equals("elenasys")) { //Correcto para comparar strings!
    ...
}

if (nombre == "elenasys") {   //Incorrecto para comparar strings!
    ...
}

Este es un error común en nuevos desarrolladores.

Jorgesys
  • 103,630
  • 13
  • 52
  • 124
16

Para complementar las otras respuestas te diré que no hay una regla para comparar la igualdad de una cadena, tanto el método .equals() como el operador == son totalmente validos, la diferencia depende de lo que quieras comparar o de cual sea realmente tu intención al querer comparar dos objetos String, (por referencia o por valor).

Las cadenas son Objetos como cualquiera pero hay grandes y sutiles diferencias que lo apartan del resto de Objetos, antes que nada a una cadena la puedes inicializar de estas dos formas.

String usandoLiteral = "Mi literal"; // Por medio de un literal
String usandoNew = new String("Con operador new"); // Utilizando el operador clasico new

En ambos casos se esta creando un objeto, la ausencia del operador new en el primer ejemplo es solamente un atajo (así se ve mas natural).

Ahora hay que comprender como Java gestiona las cadenas, en Java existe el String Pool este es un espacio de memoria reservado para almacenar las cadenas, una aplicación normalmente hace un fuerte uso de los Strings y solamente estos pueden llegar a ocupar hasta un 40% de la memoria en runtime y muchos de estos String's son repetidos por ejemplo 'a' 'en' 'la' etc. Para optimizar esto Java colecciona todas estas cadenas como literales en una ubicación de la JVM y reutiliza los literales repetidos para optimizar el uso de la memoria, de este modo si almacenas el literal "carlos" en dos variables, Java reutilizara este literal.

Por esta razón si declaras las siguientes variables String y las inicializas con el mismo literal. (recuerda que estas creando dos Objetos)

String nombre = "carlos";
String nombre2 = "carlos";

La comparación con el operador == retornará true, porque el String Pool recicla el literal "carlos" y por ende son la misma referencia, por lo tanto es completamente valido hacer la comparación.

Boolean sonIguales = nombre == nombre2; // sonIguales tiene el valor true

El String Pool solamente recicla y almacena literales, esto quiere decir que si creas una cadena de la siguiente manera

String nombre3 = new String("carlos");

Al no ser inicializado con un literal java no lo almacenara en el String Pool ni habrá reciclaje por lo tanto será eliminado por el Garbage Collector, si esto es asi entonces la siguiente comparación resultara en false.

Boolean esIgualAlTercero = nombre == nombre3; // esIgualAlTercero tiene el valor false.

Esta es la razón del porque cuando haces la comparación con el operador == algunas veces te dará true y otras te dará false a pesar de que comparas el mismo valor en la cadena.

El metodo .equals lo utilizas cuando quieres comparar la cadena por valor o carácter por carácter, es una forma segura de hacer la comparación de cadenas ya que recuerda que como en el primer ejemplo técnicamente hicimos una comparación por valor con el operador == debido a la peculiaridad del String Pool.

Una nota adicional por si te lo preguntas, si al inicializar una cadena por literal el valor esta almacenado en el String Pool que sucede cuando modifico la cadena?

Pues ese es otro punto, las cadenas en Java son inmutables, esto quiere decir que no puedes modificar su valor por ejemplo agregar mas caracteres a un valor String y Java no proporciona ningún API para modificar un String directamente. (sin embargo Java proporciona un API para poder hacerlo indirectamente)

Por ejemplo si quiero agregar el apellido a mi variable (nombre) lo que puedo hacer es re asignar a la variable (nombre) una nueva cadena que la construiré concatenando el valor original con el apellido de esta manera.

nombre = nombre + " lucero"; 

Esto lo que hace es crear un nuevo valor "carlos lucero" y asignar este valor a la cadena nombre.

Ahora que sucede si ahora comparo 'nombre' con una nueva variable que contenga el mismo valor que 'nombre' pero asignada como literal?

String nombreCompleto = "carlos lucero";
Boolean esNombreCompletoIgual = nombreCompleto == nombre; // recuerda que nombre ahora tiene el valor "carlos lucero"

Pues como la variable (nombre) fue re asignada a un nuevo valor que no fue creado por medio de un literal pues ahora la variable (nombre) ya no esta en el String Pool por lo tanto su referencia no es reciclada y la comparación con la variable (nombreCompleto) que si fue creado por medio de literal dará como resultado False.

Por ultimo Java proporciona un API para solventar el problema de inmutabilidad de las cadenas, esta API esta expuesta por la clase StringBuilder.

Stefan Nolde
  • 6,648
  • 1
  • 24
  • 44
Carlos Lucero
  • 1,270
  • 6
  • 13
  • 1
    Aunque esto no responde al OP, la he votado porque explica cómo funcionan las cadenas (objetos "String") en Java. – Jose4Linux Jan 01 '17 at 22:16
  • @Jose4Linux así es, ya respondieron cuando usar y cuando no, quería complementar explicando el porque y porque a veces puede funcionar el operador == y a veces no, gracias pro el voto – Carlos Lucero Jan 01 '17 at 23:56
  • Cuando te refieres a respuestas más antiguas, por favor ten en cuenta que "anteriores" puede confundir en el caso que el orden de las respuestas puede cambiar por evaluaciones. – Stefan Nolde Jan 18 '17 at 06:02
11

Como ya mencionado, el operador == constate en el caso de primitivos que los valores son identicos, en caso de objetos que la referencia apunta al mismo objeto.

Falta explicar un poco sobre el método equals() Todas las clases en Java son subclases de Object, object viene con una declaracion por defecto de equals().

Si declaro una nueva clase, otros usuarios de mi clase esperan que implemento un método equals(Object o) que implica que dos objetos se consideran iguales. Eso no necesariamente significa que los son, y puede requerir comentarios en la documentación. Un ejemplo:

public class Point(){
    private int x;
    private int y;

    public Point(intx, int y){
        this.x=x;
        this.y=y;
    }

    public int getX() {return x;}

    public int getY() {return x;}

    @Override
    public boolean equals(Object o){
        return o instanceof Point && ((Point)o).getX()==x && ((Point)o).getY()==y;
    }
}

Si declaro una subclase de Point con etiqueta como:

public class NamedPoint extends Point{

    private String etiqueta;

    public NamedPoint(int x, int y, String etiqueta){
        super(x,y);
        this.etiqueta=etiqueta;
    }

    public String getEtiqueta(){ return etiqueta }
}

debería comentar que dos NamedPoint se consideran iguales si los coordinados corresponden, independiente de la etiqueta.

Ojo, simplemente sobreescribir el método no asegura coherencia en mi lógica. Si sobreescribo equals en NamedPoint para también considerar las etiquetas, me queda con el siguiente problema:

Point point = new Point(3,6);
Point otroPoint = new NamedPoint(3,6,"Foo");
boolean equals1 = point.equals(otroPoint); // resultado true
boolean equals2 = otroPoint.equals(point); // resultado false

para escapar esta ambigüedad se pueden comparar las clases en vez de usar instanceof:

    @Override
    public boolean equals(Object o){
        return this.getClass().equals(o.getClass()) 
            && ((Point)o).getX()==x && ((Point)o).getY()==y;
    }

Resumen: hay dragones en el tema de la igualdad.

Stefan Nolde
  • 6,648
  • 1
  • 24
  • 44
9

"==" se usa para un Integer ,Char, Object, o null, entre otros, mejor usa equals o equalsIgnoreCase:

 final String miNombre = "Jordi";
Scanner input = new Scanner(System.in);
System.out.println("Adivina mi nombre: ");

while (true) {
    String intento = input.next();
    if(intento.equalsIgnoreCase(miNombre)) { //Tu puedes usar equals para que sea igual y equalsIgnoreCase para que se igual ignorando las mayúsculas && minúsculas, es decir que por mas que escribas JoRdI va a ser "true".
        System.out.println("Acertaste!");
        break;
    } else {
        System.out.println("Intentalo de nuevo!");
    }
}
Java doub
  • 457
  • 3
  • 9
  • La explicación donde se usa '==' es atrevido. Hay muchos usos para comparar _Object_ con _equals()_, probablemente más que para obj1==obj2. – Stefan Nolde Jan 08 '17 at 06:53
9

El operador == suele funcionar cuando la variable ya ha sido inicializada, pues compara las representaciones internas, pero lo más correcto es comparar usando el método equals que se hereda de la clase Object.

Compararlas mediante equals casi siempre es la mejor solución

String cadena1="Hola";
String cadena2="Hola";
System.out.println(cadena1.equals(cadena2)); //funciona

String cadena1;
String cadena2="Hola";
System.out.println(cadena1.equals(cadena2)); //funciona

Pero a pesar de ello podrías tener problemas si tu máquina virtual es menor a la Tiger, pues en implementaciones aciagas una cadena definida como null no era lo mismo que una cadena vacía.

String cadena1="";
String cadena2=null;
String cadena3;
System.out.println(cadena1.equals(cadena2).equals(cadena3));

Así que para esos casos puedes usar la comparación mediante el operador ==

String cadena1="Hola";
String cadena2="Hola";
System.out.println(cadena1 == cadena2); //funciona porque ya están inicializadas

O teniendo mucha reserva, comparando las representaciones internas.

String cadena1="Hola";
String cadena2="Hola";
System.out.println(cadena1.intern() == cadena2.intern()); //son iguales solamente si cadena1.equals(cadena2)
Ruslan López
  • 10,060
  • 11
  • 35
  • 68
8

Sólo para tipos primitivos la comparación == es válida en general. Para objetos se deben utilizar métodos de comparación como equals() que podría ser válido para Strings. Para otros tipos de objetos podría no ser válido este tipo de comparación.

Con == se comparan referencias y no contenido de los objetos en memoria.

Jorgesys
  • 103,630
  • 13
  • 52
  • 124
joseluque
  • 229
  • 2
  • 3
8

La forma correcta de comparar 2 Strings es mediante la función equals(), que es para comparar las 2 cadenas y equalsIngnoreCase() que es lo mismo pero ignorando las mayúsculas y minúsculas.

Prueba con:

if(intento.equalsIgnoreCase(miNombre)){
     System.out.println("Has Acertado");
}
else{
     System.out.println("Incorrecto");
}
Mariano
  • 23,777
  • 20
  • 70
  • 102
Alexis Rodriguez
  • 885
  • 4
  • 18
  • 30
7

En java la forma correcta para comparar Strings es utilizando el metodo equals el cual es heredado de la clase Object

ejemplo:

String str1 = "hola"
String str2 = new String("hola");
String str3 = "hola";
System.out.println(str1 == str2); //falso 
System.out.println(str1 == str3); //verdadero
System.out.println(str1.equals(str2)); //verdadero
System.out.println(str2.equals(str3)); //verdadero

Al declarar un String de esta forma : String str = "cadena"; el objeto es un literal (valor declarado al momento de escribir el codigo) y es almacenado en el String pool (piscina donde se almacenan una sola vez las cadenas y son leidas varias veces por los literales)

por lo que :

"cadena" es distinto a new String("cadena") ;

ya que esta ultima se genera en tiempo de ejecucion y no esta en el String pool.

5

Puedes implementar el método String.equals(""); que la misma API de java trae así como también debes de implementar las validaciones correspondientes como son: realizar el cadena.trim(); al texto que deseas validar ya que un espacio tambien cuenta a la hora de la comparación. este método lo que realiza es quitar los espacios al final y al principio de la cadena. también puedes incluir cadena.lengt(); para realizar tus validaciones todo depende de tus necesidades. saludos ejemplo Sencillo :

String micadena="Hola mundo";
String cadenaGuardada="Hola mundo";

if(micadena.equals(cadenaGuardada))
return "iguales";
else
return "No son iguales";