Comparación de cadenas en Java: guía completa | diferencias y uso de ==, equals y compareTo

目次

1. Introducción

¿Por qué es importante comparar cadenas en Java?

En la programación con Java, el manejo de cadenas (String) es extremadamente frecuente. Desde la verificación de nombres de usuario, la validación de valores ingresados en formularios, hasta la comprobación de respuestas de APIs, en cualquier caso se necesita comparar cadenas.

En este contexto, ¿cómo comparar correctamente las cadenas? es un punto donde los principiantes suelen tropezar. En particular, si no se entiende la diferencia entre el operador == y el método equals(), se pueden generar bugs que producen resultados inesperados.

El peligro de no distinguir entre “==” y “equals”

Por ejemplo, observen el siguiente fragmento de código:

String a = "apple";
String b = new String("apple");

System.out.println(a == b);       // 結果: false
System.out.println(a.equals(b));  // 結果: true

Muchos se sorprenderán al ver el resultado de este código. Aunque las cadenas son idénticas, == devuelve false y equals() devuelve true. Esto ocurre porque Java trata a las cadenas como tipos de referencia, y == compara la dirección de memoria (referencia) de los objetos.

Así, comparar correctamente las cadenas impacta directamente en la fiabilidad y legibilidad del programa. En otras palabras, si se comprende el método correcto, se pueden evitar bugs desde el principio.

Qué aprenderás en este artículo

En este artículo explicaremos, paso a paso, los métodos para comparar cadenas en Java, desde lo básico hasta casos más avanzados. Responderemos a preguntas como las siguientes, con un enfoque claro y accesible para principiantes:

  • ¿Cuál es la diferencia entre == y equals()?
  • ¿Cómo comparar ignorando mayúsculas y minúsculas?
  • ¿Cómo comparar cadenas en orden lexicográfico?
  • ¿Cómo evitar excepciones al comparar con null?

A través de ejemplos de código útiles en entornos profesionales, adquirirás conocimientos sólidos sobre la comparación correcta de cadenas.

2. Conceptos básicos de las cadenas en Java

Las cadenas son “tipos de referencia”

En Java, el tipo String no es un tipo primitivo (como int o boolean), sino un tipo de referencia. Esto significa que una variable String no contiene directamente los datos de la cadena, sino que apunta a un objeto de cadena almacenado en el heap.

Por ejemplo, si escribimos lo siguiente:

String a = "hello";
String b = "hello";

a y b hacen referencia a la misma cadena "hello", por lo que a == b puede devolver true. Esto se debe al mecanismo de internado de cadenas (String Interning) que Java aplica a los literales.

Diferencia entre literales de cadena y new String()

Cuando se reutiliza el mismo literal de cadena varias veces, Java lo optimiza para que todas las referencias apunten al mismo objeto. Esta característica permite compartir la cadena en tiempo de ejecución y mejorar la eficiencia de memoria.

String s1 = "apple";
String s2 = "apple";
System.out.println(s1 == s2); // true(同じリテラルなので同一参照)

En cambio, si se crea explícitamente un objeto con la palabra clave new, se genera una nueva referencia independiente.

String s3 = new String("apple");
System.out.println(s1 == s3); // false(異なる参照)
System.out.println(s1.equals(s3)); // true(内容は同じ)

De esta forma, == verifica si las referencias son idénticas, mientras que equals() comprueba si el contenido es idéntico, por lo que sus usos difieren considerablemente.

String es una clase “inmutable”

Otro aspecto importante es que String es inmutable: una vez creado un objeto String, su contenido no puede modificarse.

Por ejemplo, al escribir lo siguiente:

String original = "hello";
original = original + " world";

Parece que se está añadiendo texto a la variable original, pero en realidad se crea un nuevo objeto String que luego se asigna a original.

Esta inmutabilidad hace que String sea thread‑safe y favorece la seguridad, así como la optimización de cachés.

3. Métodos para comparar cadenas

Comparación de referencias con el operador ==

== compara las referencias (direcciones) de los objetos String. Por lo tanto, aunque dos cadenas tengan el mismo contenido, si son objetos diferentes, == devolverá false.

String a = "Java";
String b = new String("Java");

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

En este ejemplo, a es un literal y b se crea con new; sus referencias difieren, por lo que el resultado es false. No debe usarse para comparar contenido.

Comparación de contenido con el método equals()

equals() es el método correcto para comparar el contenido de dos cadenas y se recomienda en la gran mayoría de los casos.

String a = "Java";
String b = new String("Java");

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

Aunque las referencias sean distintas, si el contenido coincide, equals() devuelve true.

Precaución al comparar con null

El siguiente fragmento puede lanzar una NullPointerException:

String input = null;
System.out.println(input.equals("test")); // 例外発生!

Para evitarlo, se recomienda escribir la comparación como constante.equals(variable), garantizando que el método se invoque sobre un objeto no nulo.

System.out.println("test".equals(input)); // false(安全)

Comparación sin distinguir mayúsculas/minúsculas con equalsIgnoreCase()

Cuando se necesita comparar nombres de usuario, correos electrónicos, etc., sin importar mayúsculas o minúsculas, equalsIgnoreCase() resulta muy útil.

String a = "Hello";
String b = "hello";

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

No obstante, en algunos casos especiales de Unicode (por ejemplo, la letra turca “İ”) el comportamiento puede ser inesperado; en aplicaciones internacionalizadas se deben considerar ajustes adicionales.

Comparación lexicográfica con compareTo()

compareTo() compara dos cadenas según el orden lexicográfico y devuelve un entero con el siguiente significado:

  • 0 → las cadenas son iguales
  • Valor negativo → la cadena que invoca el método precede a la otra (es “menor”)
  • Valor positivo → la cadena que invoca el método sigue a la otra (es “mayor”)
    String a = "apple";
    String b = "banana";
    
    System.out.println(a.compareTo(b)); // 負の値("apple"は"banana"より前)
    

Este método se usa frecuentemente para ordenar colecciones (Collections.sort()) o para comparar claves en estructuras como TreeMap.

4. Ejemplos prácticos

Verificación de entrada de usuario (funcionalidad de login)

Uno de los casos de uso más comunes es comprobar la coincidencia de nombre de usuario y contraseña.

String inputUsername = "Naohiro";
String registeredUsername = "naohiro";

if (registeredUsername.equalsIgnoreCase(inputUsername)) {
    System.out.println("ログイン成功");
} else {
    System.out.println("ユーザー名が一致しません");
}

En situaciones donde se desea comparar sin distinguir mayúsculas y minúsculas, como en este ejemplo, es apropiado usar equalsIgnoreCase().

Sin embargo, por motivos de seguridad, la comparación de contraseñas debe distinguir mayúsculas y minúsculas, por lo que se debe usar equals().

Validación de entrada (procesamiento de formularios)

Por ejemplo, la verificación de valores ingresados desde menús desplegables o cuadros de texto también utiliza comparaciones de cadenas.

String selectedOption = request.getParameter("plan");

if ("premium".equals(selectedOption)) {
    System.out.println("プレミアムプランを選択しました。");
} else {
    System.out.println("その他のプランです。");
}

De esta manera, la forma "CONST".equals(variable) se usa frecuentemente en la práctica como una comparación segura que también verifica null. Dado que la entrada del usuario no siempre contiene un valor, esta escritura previene NullPointerException.

Procesamiento de ramificaciones con múltiples condiciones (uso similar a switch)

Cuando se desea manejar múltiples candidatos de cadena en una ramificación condicional, es común usar equals() de forma consecutiva.

String cmd = args[0];

if ("start".equals(cmd)) {
    startApp();
} else if ("stop".equals(cmd)) {
    stopApp();
} else {
    System.out.println("コマンドが不正です");
}

A partir de Java 14, la sentencia switch también se puede usar oficialmente con cadenas.

switch (cmd) {
    case "start":
        startApp();
        break;
    case "stop":
        stopApp();
        break;
    default:
        System.out.println("不明なコマンドです");
}

Así, la comparación de cadenas está directamente vinculada al procesamiento de lógica de ramificación, por lo que se requiere una comprensión precisa.

Errores que ocurren al comparar con null y sus contramedidas

Un ejemplo frecuente de error es que la aplicación se bloquee al comparar con un valor null.

String keyword = null;

if (keyword.equals("検索")) {
    // 例外発生:java.lang.NullPointerException
}

En tales casos, se puede comparar de forma segura escribiendo lo siguiente.

if ("検索".equals(keyword)) {
    System.out.println("検索実行");
}

También es posible realizar primero una verificación de null más estricta.

if (keyword != null && keyword.equals("検索")) {
    System.out.println("検索実行");
}

El código seguro frente a null es una habilidad esencial para aumentar la robustez.

5. Rendimiento y optimización

Costo de procesamiento en la comparación de cadenas

equals() y compareTo() están optimizados para funcionar rápidamente en general, pero internamente comparan carácter por carácter, por lo que pueden afectar al manejar cadenas largas o grandes volúmenes de datos. En particular, comparar repetidamente la misma cadena dentro de un bucle puede provocar una degradación de rendimiento no deseada.

for (String item : items) {
    if (item.equals("keyword")) {
        // 比較回数が多い場合、注意
    }
}

Aceleración de comparaciones con String.intern()

Al usar el método String.intern() de Java, se registra la cadena con el mismo contenido en el «pool de cadenas» de la JVM, lo que permite compartir referencias. Esto hace posible comparar con ==, lo que puede ofrecer ventajas de rendimiento.

String a = new String("hello").intern();
String b = "hello";

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

Sin embargo, abusar del pool de cadenas puede comprimir el área del heap, por lo que debe limitarse a usos restringidos.

Trampas de equalsIgnoreCase() y alternativas

equalsIgnoreCase() es conveniente, pero implica una conversión de mayúsculas/minúsculas durante la comparación, lo que puede hacer que sea ligeramente más costoso que el equals() normal. En escenarios donde el rendimiento es crítico, comparar valores ya normalizados a mayúsculas o minúsculas es más rápido.

String input = userInput.toLowerCase();
if ("admin".equals(input)) {
    // 高速化された比較
}

De esta manera, convertir previamente y luego usar equals() mejora la eficiencia del proceso de comparación.

Uso de StringBuilder / StringBuffer

En casos donde se generan muchas concatenaciones de cadenas, usar String crea un nuevo objeto en cada operación, aumentando la carga de memoria y CPU. Incluso cuando se mezclan comparaciones, la mejor práctica es usar StringBuilder para concatenar y construcción, y mantener String para las comparaciones.

StringBuilder sb = new StringBuilder();
sb.append("user_");
sb.append("123");

String result = sb.toString();

if (result.equals("user_123")) {
    // 比較処理
}

Diseño para acelerar mediante caché y preprocesamiento

Cuando se compara repetidamente con la misma cadena, es útil almacenar en caché el resultado de la comparación o preprocesar usando un mapa (como HashMap) para reducir la cantidad de comparaciones.

Map<String, Runnable> commandMap = new HashMap<>();
commandMap.put("start", () -> startApp());
commandMap.put("stop", () -> stopApp());

Runnable action = commandMap.get(inputCommand);
if (action != null) {
    action.run();
}

De este modo, la comparación de cadenas con equals() se sustituye por una única búsqueda en el mapa, mejorando tanto la legibilidad como el rendimiento.

6. Preguntas frecuentes (FAQ)

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

A.
== realiza una comparación de referencias (es decir, verifica si las direcciones de memoria son idénticas). Por otro lado, equals() compara el contenido interno de la cadena.

String a = new String("abc");
String b = "abc";

System.out.println(a == b);        // false(参照が異なる)
System.out.println(a.equals(b));   // true(内容は同じ)

Por lo tanto, siempre use equals() cuando desee comparar el contenido de las cadenas.

Q2. ¿Por qué al usar equals() pueden producirse errores por null?

A.
Llamar a un método sobre null genera una NullPointerException.

String input = null;
System.out.println(input.equals("test")); // 例外発生!

Para evitar este error, es seguro escribir la comparación desde el lado de la constante, como se muestra a continuación.

System.out.println("test".equals(input)); // false(安全)

Q3. ¿Cómo comparar sin distinguir mayúsculas y minúsculas?

.A.
Al usar el método equalsIgnoreCase(), se pueden comparar cadenas ignorando la diferencia entre mayúsculas y minúsculas.

String a = "Hello";
String b = "hello";

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

Sin embargo, con caracteres de ancho completo o algunos caracteres Unicode especiales, el resultado puede ser inesperado, por lo que se debe tener cuidado.

Q4. ¿Cómo comparar cadenas por su orden?

A.
Cuando se desea investigar la relación de orden lexicográfico de las cadenas, se usa compareTo().

String a = "apple";
String b = "banana";

System.out.println(a.compareTo(b)); // 負の値("apple"は"banana"より前)

Significado del valor de retorno:

  • 0 → Igual
  • Valor negativo → La cadena de la izquierda es anterior
  • Valor positivo → La cadena de la izquierda es posterior

Se utiliza, por ejemplo, en procesos de ordenación.

Q5. ¿Cuáles son las mejores prácticas que debes recordar al comparar cadenas?

A.

  • Siempre usa equals() para la comparación de contenido.
  • Ten en cuenta la seguridad frente a null usando la forma "constante".equals(variable).
  • Para ignorar mayúsculas/minúsculas, usa equalsIgnoreCase() o realiza previamente toLowerCase() / toUpperCase().
  • En escenarios que requieran comparaciones masivas o alta velocidad, considera intern() o diseños de caché.
  • Mantén siempre en mente el equilibrio entre legibilidad y seguridad.

7. Resumen

En Java, es importante “usar correctamente” la comparación de cadenas

A lo largo de este artículo, hemos explicado de forma integral los fundamentos, la práctica y el rendimiento de la comparación de cadenas en Java. Dado que String es un tipo de referencia, un método de comparación incorrecto puede producir comportamientos inesperados en varios casos.

En particular, la diferencia entre == y equals() es un punto que confunde a los principiantes. Comprenderla y usarla correctamente es esencial para escribir código seguro y confiable.

Lo que aprendiste en este artículo (lista de verificación)

  • == es un operador que compara referencias (direcciones de memoria).
  • equals() es el método que compara el contenido de las cadenas (el más seguro).
  • Con equalsIgnoreCase() puedes ignorar mayúsculas y minúsculas.
  • Con compareTo() es posible comparar cadenas por orden lexicográfico.
  • Usando "constante".equals(variable) se compara de forma segura contra null.
  • Si se busca rendimiento, intern() o diseños de caché también son útiles.

Conocimientos de comparación de cadenas útiles en el trabajo

La comparación de cadenas está estrechamente vinculada a tareas de desarrollo cotidianas, como la verificación de inicio de sesión, validación de entradas, búsquedas en bases de datos y estructuras condicionales. Con este conocimiento, podrás evitar errores y lograr que el código se comporte según lo esperado.

En el futuro, al escribir código que maneje cadenas, te recomendamos consultar este artículo y elegir el método de comparación que mejor se ajuste a tu objetivo.