Artículo

== vs equals() en Java: Diferencias y Cuándo Usar Cada Uno

Aprende la diferencia real entre == y equals() en Java con ejemplos de Strings, objetos propios y el caso del caché de Integer.

En Java, == y equals() sirven para comparar, pero no hacen lo mismo: == compara referencias de memoria (o valores, en el caso de primitivos), mientras que equals() compara el contenido lógico de dos objetos según cómo lo defina su clase.

¿Qué hace realmente == en Java?

Con tipos primitivos (int, double, boolean...), == compara directamente los valores. Con objetos, == compara si ambas variables apuntan exactamente a la misma dirección de memoria.

int a = 5;
int b = 5;
System.out.println(a == b); // true, compara valores

¿Qué hace equals()?

equals() es un método heredado de Object que, por defecto, se comporta igual que == (compara referencias). Muchas clases de Java, como String, lo sobreescriben para comparar el contenido en lugar de la referencia.

== vs equals() con tipos primitivos

Con primitivos solo existe ==, ya que no son objetos y no tienen métodos. No se puede llamar a .equals() sobre un int directamente (a menos que esté autoboxeado a Integer).

== vs equals() con Strings (el caso que confunde a todos)

String s1 = "hola";
String s2 = "hola";
String s3 = new String("hola");

System.out.println(s1 == s2);        // true (mismo literal, mismo pool de Strings)
System.out.println(s1 == s3);        // false (objetos distintos en memoria)
System.out.println(s1.equals(s3));   // true (mismo contenido)

Los literales de String se almacenan en un String Pool, por lo que dos literales idénticos pueden compartir referencia. Pero un String creado con new siempre genera un objeto nuevo en memoria. La recomendación es siempre usar equals() para comparar contenido de Strings, nunca ==.

== vs equals() con objetos propios

class Punto {
    int x, y;
    Punto(int x, int y) { this.x = x; this.y = y; }
}

Punto p1 = new Punto(1, 2);
Punto p2 = new Punto(1, 2);

System.out.println(p1 == p2);       // false, referencias distintas
System.out.println(p1.equals(p2));  // false, porque no hemos sobreescrito equals()

Sin sobreescribir equals(), la clase hereda el comportamiento por defecto de Object, que compara referencias, igual que ==.

Sobreescribir equals() y hashCode() correctamente

class Punto {
    int x, y;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Punto)) return false;
        Punto p = (Punto) o;
        return x == p.x && y == p.y;
    }

    @Override
    public int hashCode() {
        return Objects.hash(x, y);
    }
}

La regla es estricta: si sobreescribes equals(), siempre debes sobreescribir también hashCode(). Estructuras como HashMap o HashSet usan hashCode() para ubicar el objeto y luego equals() para confirmar la igualdad; si solo sobreescribes uno de los dos, esas colecciones pueden comportarse de forma inconsistente.

Desde Java 16, los record generan automáticamente equals() y hashCode() coherentes basados en sus componentes, evitando este problema por completo.

Tabla resumen: cuándo usar == y cuándo equals()

Tipo de datoUsa ==Usa equals()
Primitivos (int, double...)No aplica
StringNo
Objetos propiosSolo para comprobar identidad exactaSí, para comparar contenido
EnumsSí (es seguro, referencia única por constante)También válido

Errores comunes (incluyendo el caché de Integer -128 a 127)

Integer a = 100;
Integer b = 100;
System.out.println(a == b); // true, por el caché interno de Integer

Integer c = 200;
Integer d = 200;
System.out.println(c == d); // false, fuera del rango cacheado

Java cachea internamente los objetos Integer entre -128 y 127 por eficiencia. Esto provoca que == funcione "por casualidad" en ese rango y falle fuera de él, un error clásico y muy citado en entrevistas técnicas. La solución, de nuevo, es usar siempre equals() para comparar wrappers.

Preguntas frecuentes

¿Por qué == funciona a veces con Strings? Porque los literales de String comparten el String Pool. Pero es un comportamiento poco fiable: nunca debe usarse como método de comparación intencional.

¿Qué pasa si sobreescribo equals() pero no hashCode()? La clase puede comportarse de forma incorrecta dentro de estructuras como HashMap o HashSet, ya que ambos métodos deben ser coherentes entre sí.

¿Los enums se comparan con == o equals()? Con == es seguro, ya que cada constante de un enum es única en la JVM. Ambas opciones son válidas.

Siguiente paso

Comprender bien esta diferencia es clave antes de trabajar con estructuras de datos como HashMap, o antes de definir correctamente un Java Bean con sus propios métodos equals() y hashCode().

java equals hashcode comparacion de objetos

Newsletter Semanal de Java

Cada viernes recibe lo más nuevo del ecosistema Java: frameworks, herramientas y mejores prácticas.

🔒 Sin spam. Cancela cuando quieras.

Compartir artículo