Operadores de comparación en Java explicados: ==, !=, <, > y diferencias de equals()

目次

1. Qué aprenderá en este artículo (Conclusiones clave primero)

En Java, los operadores de comparación son características fundamentales del lenguaje que se usan para comparar valores como números y caracteres.
Sin embargo, muchos principiantes tienen dificultades al comparar objetos como String o Integer, sobre todo cuando utilizan el operador == de forma incorrecta.

Esta sección resume los puntos clave al inicio, para que pueda entender rápidamente cuándo los operadores de comparación son seguros de usar y cuándo no lo son.

1.1 Los operadores de comparación se dividen en dos categorías

Los operadores de comparación de Java pueden agruparse en dos tipos principales:

  • Operadores relacionales (comparación de orden) <, <=, >, >=
  • Operadores de igualdad (comparación de igualdad) ==, !=

Al trabajar con tipos primitivos (como int, double o char), estos operadores se comportan exactamente como se espera.

int a = 10;
int b = 20;

System.out.println(a < b);   // true
System.out.println(a == b);  // false

1.2 Importante: == NO siempre compara valores

Esta es la fuente de confusión más frecuente en Java.

  • Para tipos primitivos, == compara los valores reales.
  • Para tipos de referencia (objetos), == compara si ambas variables apuntan al mismo objeto.

Esta diferencia es crítica.

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

System.out.println(s1 == s2);      // false
System.out.println(s1.equals(s2)); // true

Aunque el texto se vea idéntico, s1 y s2 son objetos diferentes en memoria.

👉 Regla práctica:
Si desea comprobar si dos objetos tienen el mismo contenido, use equals().

1.3 Guía rápida de decisiones (Hoja de trucos)

Si solo recuerda una cosa, recuerde esto:

  • Valores primitivos (int, boolean, char, etc.) → Use operadores de comparación (==, <, >, etc.)
  • Comparación de contenido de objetos (String, Integer, objetos personalizados) → Use equals() o Objects.equals()
  • Orden o clasificación (qué es mayor/menor) → Use compareTo() o un Comparator

Insight clave:
== puede parecer simple, pero para objetos responde
“¿Son el mismo objeto?”, no
“¿Tienen el mismo valor?”

1.4 Qué podrá hacer después de leer este artículo

Al terminar este artículo, podrá:

  • Utilizar los operadores de comparación de Java de forma correcta y segura
  • Evitar errores comunes provocados por == vs equals()
  • Entender por qué las comparaciones de Integer a veces se comportan de manera inconsistente
  • Elegir el enfoque adecuado al comparar valores, objetos o orden

En la siguiente sección, comenzaremos con una visión completa de todos los operadores de comparación de Java, antes de profundizar en los errores comunes y las mejores prácticas.

2. Operadores de comparación en Java (Lista completa)

En esta sección organizaremos todos los operadores de comparación de Java y aclararemos qué pueden y qué no pueden comparar.
El objetivo es eliminar ambigüedades antes de abordar casos más complejos.

2.1 Los operadores de comparación siempre devuelven un booleano

Todo operador de comparación en Java devuelve un valor de tipo boolean:

  • true → la condición se cumple
  • false → la condición no se cumple

Por eso los operadores de comparación se usan con frecuencia en if, while y otras sentencias de control.

int x = 5;
int y = 10;

boolean result = x < y;  // true

Los operadores de comparación no modifican valores, solo evalúan condiciones.

2.2 Operadores relacionales: <, <=, >, >=

Estos operadores comparan orden o magnitud.
Se utilizan principalmente con tipos numéricos y char.

OperatorMeaning
<less than
<=less than or equal to
>greater than
>=greater than or equal to

Ejemplo con números

int a = 10;
int b = 20;

System.out.println(a < b);   // true
System.out.println(a >= b);  // false

Ejemplo con char

Los caracteres se comparan usando sus valores Unicode.

char c1 = 'A';
char c2 = 'B';

System.out.println(c1 < c2); // true

Nota:
Estos operadores no pueden usarse con String. Hacerlo produce un error en tiempo de compilación.

2.3 Operadores de igualdad: == y !=

Los operadores de igualdad verifican si dos operandos son iguales o no.

OperatorMeaning
==equal to
!=not equal to

Uso seguro con tipos primitivos

int x = 5;
int y = 5;

System.out.println(x == y); // true
System.out.println(x != y); // false

Aquí, Java compara valores reales, lo cual es directo y seguro.

2.4 Comparando valores boolean

Los valores boolean también pueden compararse usando == y !=.

boolean f1 = true;
boolean f2 = false;

System.out.println(f1 == f2); // false

En código real, sin embargo, es más legible escribir:

if (isEnabled) {
    // do something
}

en lugar de:

if (isEnabled == true) { ... }

2.5 Tipos que funcionan bien con los operadores de comparación

Seguro usar los operadores de comparación directamente:

  • int , long , double , float
  • char
  • boolean (solo == / != )

No seguro o no permitido:

  • String
  • Clases wrapper ( Integer , Long , etc.)
  • Objetos personalizados

Estos tipos requieren técnicas de comparación diferentes, que cubriremos a continuación.

2.6 Conclusión clave de esta sección

  • Los operadores de comparación siempre devuelven true o false
  • Funcionan de forma fiable con tipos primitivos
  • Usarlos con objetos puede generar errores o fallos de compilación

En la siguiente sección, nos enfocaremos en tipos primitivos, donde los operadores de comparación se comportan exactamente como se espera.

3. Operadores de comparación con tipos primitivos (zona segura)

Los tipos primitivos son el caso más seguro y sencillo para los operadores de comparación.
Comprender claramente esta sección ayuda a reconocer cuándo las cosas empiezan a complicarse.

3.1 ¿Qué son los tipos primitivos?

Los tipos primitivos almacenan valores reales, no referencias.

Ejemplos comunes incluyen:

  • Tipos numéricos: int , long , double , float
  • Tipo de carácter: char
  • Tipo booleano: boolean

Como no hay referencias a objetos involucradas, las comparaciones se comportan de manera predecible.

3.2 Comparando valores enteros y long

int a = 100;
int b = 100;

System.out.println(a == b); // true
System.out.println(a < b);  // false

Java compara los valores numéricos directamente.

Tipos numéricos mixtos

int x = 10;
long y = 10L;

System.out.println(x == y); // true

Java realiza promoción automática de tipos antes de la comparación.

3.3 Comparando caracteres (char)

Aunque char representa un carácter, Java lo trata internamente como un número.

char c1 = 'A';
char c2 = 'a';

System.out.println(c1 < c2); // true

Esta comparación se basa en valores Unicode, no en reglas alfabéticas de un lenguaje humano.

3.4 Comparando valores booleanos

boolean flag1 = true;
boolean flag2 = false;

System.out.println(flag1 != flag2); // true

En la práctica, evite comparaciones redundantes:

if (isLoggedIn) { ... }      // preferred
if (isLoggedIn == true) { } // unnecessary

3.5 Trampa al comparar números de punto flotante (double / float)

Esta es una trampa clásica de Java.

double d1 = 0.1 + 0.2;
double d2 = 0.3;

System.out.println(d1 == d2); // may be false

Los números de punto flotante se almacenan con limitaciones de precisión.

Enfoque recomendado: usar una tolerancia (epsilon)

double epsilon = 0.000001;

if (Math.abs(d1 - d2) < epsilon) {
    // treat as equal
}

Para cálculos financieros o de alta precisión, considere BigDecimal.

3.6 Resumen de la zona segura

  • Los tipos primitivos pueden compararse directamente
  • Las comparaciones de char usan valores Unicode
  • La igualdad de punto flotante requiere cuidados especiales
  • Hasta este punto, los operadores de comparación se comportan de forma intuitiva

A continuación, pasaremos a la zona de peligro:
por qué usar == con objetos conduce a resultados inesperados.

4. Por qué usar == con objetos causa problemas

Esto es donde muchos principiantes de Java—e incluso desarrolladores intermedios—encuentran problemas.
El comportamiento de == cambia una vez que empiezas a trabajar con tipos de referencia (objetos).

4.1 == compara referencias de objetos, no contenido

Para los objetos, el operador == verifica si ambas variables apuntan al mismo objeto en memoria.

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

System.out.println(s1 == s2); // false

Aunque ambas cadenas se vean idénticas, son objetos diferentes, por lo que la comparación falla.

4.2 equals() compara el contenido del objeto

El método equals() está diseñado para comparar igualdad lógica, es decir, el contenido real de los objetos.

System.out.println(s1.equals(s2)); // true

La clase String sobrescribe equals() de modo que compara secuencias de caracteres, no direcciones de memoria.

Regla de oro:

  • ¿Mismo objeto? → ==
  • ¿Mismo valor/contenido? → equals()

4.3 Por qué == a veces funciona con literales de cadena

Este ejemplo confunde a muchos desarrolladores:

String a = "Java";
String b = "Java";

System.out.println(a == b); // true

Esto ocurre debido al String Pool.

  • Los literales de cadena se almacenan en un pool compartido
  • Los literales idénticos pueden referenciar el mismo objeto

Sin embargo, este comportamiento es un detalle de implementación, no algo en lo que debas confiar.

String x = "Java";
String y = new String("Java");

System.out.println(x == y);      // false
System.out.println(x.equals(y)); // true

👉 Siempre usa equals() para comparar el contenido de cadenas.

4.4 Null y equals() — Otro error común

Invocar equals() sobre una referencia nula provoca un error en tiempo de ejecución.

String str = null;
str.equals("Java"); // NullPointerException

Patrón seguro 1: Llamar a equals() sobre la constante

if ("Java".equals(str)) {
    // safe
}

Patrón seguro 2: Usar Objects.equals()

if (Objects.equals(str, "Java")) {
    // safe and clean
}

4.5 Resumen de esta sección

  • == compara referencias de objetos
  • equals() compara contenido
  • El comportamiento del String Pool puede ocultar errores
  • Siempre considera la seguridad ante null

A continuación, veremos otra trampa sutil:
comparar clases wrapper como Integer y Long.

5. Comparación de clases wrapper (Integer, Long, etc.)

Las clases wrapper parecen números, pero siguen siendo objetos.

5.1 ¿Qué son las clases wrapper?

Las clases wrapper permiten que los valores primitivos se traten como objetos.

PrimitiveWrapper
intInteger
longLong
doubleDouble
booleanBoolean

5.2 Por qué == produce resultados inconsistentes

Integer a = 100;
Integer b = 100;

System.out.println(a == b); // true
Integer x = 1000;
Integer y = 1000;

System.out.println(x == y); // false

Esto ocurre debido al caché de Integer (normalmente de -128 a 127).

El resultado de == depende del comportamiento interno de la JVM, no de la igualdad de valores.

5.3 Forma correcta de comparar valores wrapper

Usa equals() para comparar valores.

System.out.println(x.equals(y)); // true

5.4 Problemas de autoboxing y unboxing

Integer a = 100;
int b = 100;

System.out.println(a == b); // true

Esto funciona debido al unboxing automático, pero:

  • Si a es null → NullPointerException
  • La intención del código se vuelve poco clara

Una comparación explícita es más segura.

5.5 Patrones de comparación recomendados

  • Comparación de valores → equals() / Objects.equals()
  • Comparación segura ante null → Objects.equals()
  • Comparación de referencias → == (raro e intencional)

5.6 Resumen de la sección

  • Las clases wrapper son tipos de referencia
  • == es poco fiable para comparar valores
  • Usa equals() de forma consistente

A continuación, centrémonos en técnicas de comparación seguras ante null.

6. Técnicas de comparación seguras ante null

Los errores relacionados con null son extremadamente comunes en aplicaciones Java.

6.1 Reglas básicas con null

  • null == null → true
  • Llamar a un método en null → error en tiempo de ejecución
  • Operadores relacionales ( < , > ) con null → error de compilación

6.2 Patrón peligroso

str.equals("Java"); // unsafe

6.3 Patrón seguro #1: Constante primero

"Java".equals(str);

6.4 Patrón seguro #2: Objects.equals()

Objects.equals(str, "Java");

Esto maneja todos los casos de null internamente.

6.5 Cuándo usar Objects.equals()

  • Comparar variables
  • Valores que pueden ser nulos
  • Lógica condicional más limpia

6.6 Consejo de diseño: reducir el uso de null

  • Inicializar valores temprano
  • Usar Optional donde corresponda
  • Menos nulls → comparaciones más simples

6.7 Resumen de la sección

  • Nunca llame a métodos sobre referencias que pueden ser nulas
  • Prefiera utilidades de comparación a prueba de null
  • Diseñe para minimizar el uso de null

A continuación, cubriremos comparaciones de orden usando compareTo().

7. Comparando orden con compareTo()

Los operadores de comparación no pueden determinar orden para objetos.

7.1 ¿Qué es compareTo()?

compareTo() compara el orden y devuelve:

  • Negativo → menor que
  • Cero → igual
  • Positivo → mayor que

7.2 Ejemplo de ordenamiento de String

String a = "Apple";
String b = "Banana";

if (a.compareTo(b) < 0) {
    System.out.println("Apple comes first");
}

7.3 Las clases wrapper también soportan compareTo()

Integer x = 10;
Integer y = 20;

System.out.println(x.compareTo(y)); // negative

7.4 equals() vs compareTo()

  • Verificación de igualdad → equals()
  • Ordenamiento/clasificación → compareTo()

7.5 Conexión con el ordenamiento

Métodos como Collections.sort() dependen de compareTo() internamente.

7.6 Resumen de la sección

  • Los operadores de comparación no pueden comparar el orden de objetos
  • compareTo() es la herramienta correcta
  • Esencial para ordenar y colecciones ordenadas

8. Errores comunes (lista rápida)

8.1 Usar == con Strings

str1 == str2
str1.equals(str2)

8.2 Usar == con clases wrapper

Integer a == b
a.equals(b)

8.3 Comparar valores de punto flotante directamente

a == b
✅ usar tolerancia o BigDecimal

8.4 Olvidar verificaciones de null

obj.equals(x)
Objects.equals(obj, x)

8.5 Usar < con objetos

str1 < str2
str1.compareTo(str2)

9. Resumen final: cómo elegir la comparación adecuada

9.1 Guía de decisión

  • Tipos primitivos → operadores de comparación
  • Contenido de objetosequals() / Objects.equals()
  • Orden y clasificacióncompareTo() / Comparator

9.2 Mejores prácticas

  • Entienda lo que realmente significa ==
  • Siempre considere la seguridad ante null
  • Evite depender de internals de la JVM

9.3 Qué aprender a continuación

  • Operadores lógicos ( && , || )
  • Sentencias if y switch
  • Comparator para ordenamiento personalizado
  • Implementación adecuada de equals() / hashCode()

Preguntas frecuentes

Q1. ¿Cuál es la diferencia entre == y equals() en Java?

== compara referencias de objetos, mientras que equals() compara el contenido.

Q2. ¿Por qué == a veces funciona con Strings?

Debido al String Pool. No se debe confiar en este comportamiento.

Q3. ¿Cuál es la forma más segura de comparar valores que pueden ser nulos?

Use Objects.equals(a, b).

Q4. ¿Cómo comparo Strings alfabéticamente?

Use compareTo().

Q5. ¿Son suficientes los operadores de comparación en Java?

Son suficientes para los tipos primitivos, pero los objetos requieren equals() y compareTo().