Padroneggiare il confronto di stringhe in Java: differenza tra “==”, equals(), compareTo() e migliori pratiche

1. Introduzione

Perché il confronto di stringhe è importante in Java?

Nella programmazione Java, le stringhe sono usate in molte situazioni. Controllare i nomi utente, convalidare l’input dei moduli e verificare le risposte delle API richiedono tutti il confronto di stringhe.
A questo punto, “come confrontare correttamente le stringhe” è un ostacolo comune per i principianti. In particolare, non comprendere la differenza tra l’operatore == e il metodo equals() può portare a bug inaspettati.

Il pericolo di non comprendere la differenza tra “==” e “equals”

Considera il seguente codice:

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

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

Molte persone sono sorprese da questo risultato. Anche se le stringhe sono identiche, == restituisce false mentre equals() restituisce true. Questo accade perché Java tratta le stringhe come tipi di riferimento, e == confronta gli indirizzi di riferimento.
Comprendere il modo corretto di confrontare le stringhe influisce direttamente sull’affidabilità e sulla leggibilità del tuo programma. Se lo capisci correttamente, puoi prevenire i bug prima che si verifichino.

Cosa imparerai in questo articolo

Questo articolo spiega il confronto di stringhe in Java, dalle basi alle applicazioni pratiche. Risponde a domande come:

  • Qual è la differenza tra == e equals()?
  • Come confrontare le stringhe ignorando il case?
  • Come confrontare le stringhe lessicograficamente?
  • Come evitare eccezioni quando si confronta con null?

Attraverso esempi reali, otterrai una solida comprensione delle pratiche corrette di confronto delle stringhe.

2. Nozioni di base sulle stringhe in Java

Le stringhe sono tipi di riferimento

In Java, il tipo String non è un primitivo (come int o boolean) ma un tipo di riferimento. Una variabile String non memorizza i caratteri reali ma un riferimento a un oggetto memorizzato nella heap.
Ad esempio:

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

Sia a che b possono fare riferimento allo stesso letterale "hello" grazie al meccanismo di internamento delle stringhe di Java.

Differenza tra letterali di stringa e new String()

Java ottimizza i letterali di stringa ripetuti riutilizzando lo stesso riferimento:

String s1 = "apple";
String s2 = "apple";
System.out.println(s1 == s2); // true (same literal, interned)

Tuttavia, usare new crea sempre un nuovo oggetto:

String s3 = new String("apple");
System.out.println(s1 == s3); // false (different references)
System.out.println(s1.equals(s3)); // true (same content)

Quindi, == verifica il riferimento e equals() verifica il contenuto.

Le stringhe sono immutabili

Un’altra caratteristica chiave è che String è immutabile. Una volta creata, una stringa non può essere modificata.

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

Questo crea un nuovo oggetto String invece di modificare l’originale.

3. Metodi per confrontare le stringhe

Confrontare i riferimenti con ==

== confronta i riferimenti agli oggetti:

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

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

Non dovrebbe essere usato per il confronto del contenuto.

Confrontare il contenuto con equals()

equals() è il modo corretto per confrontare il contenuto delle stringhe:

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

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

Evitare NullPointerException

String input = null;
System.out.println(input.equals("test")); // Exception!

Usa lo stile costante-prima:

System.out.println("test".equals(input)); // false, safe

Ignorare il case con equalsIgnoreCase()

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

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

Confronto lessicografico con compareTo()

  • 0 → uguali
  • Negativo → il chiamante precede
  • Positivo → il chiamante segue
    String a = "apple";
    String b = "banana";
    
    System.out.println(a.compareTo(b)); // negative
    

4. Esempi pratici

Verifica del login utente

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

if (registeredUsername.equalsIgnoreCase(inputUsername)) {
    System.out.println("Login successful");
} else {
    System.out.println("Username does not match");
}

Le password dovrebbero sempre utilizzare equals() perché è richiesta la sensibilità al caso.

Validazione dell’input del form

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

if ("premium".equals(selectedOption)) {
    System.out.println("Premium plan selected.");
} else {
    System.out.println("Other plan selected.");
}

Logica di ramificazione con controlli multipli di stringhe

String cmd = args[0];

if ("start".equals(cmd)) {
    startApp();
} else if ("stop".equals(cmd)) {
    stopApp();
} else {
    System.out.println("Invalid command");
}
switch (cmd) {
    case "start":
        startApp();
        break;
    case "stop":
        stopApp();
        break;
    default:
        System.out.println("Unknown command");
}

Gestione sicura del null

String keyword = null;

if ("search".equals(keyword)) {
    System.out.println("Searching...");
}

5. Prestazioni e Ottimizzazione

Costo dei confronti di stringhe

equals() e compareTo() confrontano i caratteri internamente. Per stringhe lunghe o confronti ripetuti, ciò può influenzare le prestazioni.

Utilizzo di String.intern() per le prestazioni

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

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

Utilizzare solo quando necessario per evitare pressione sulla memoria.

Impatto sulle prestazioni di equalsIgnoreCase()

String input = userInput.toLowerCase();
if ("admin".equals(input)) {
    // fast comparison
}

Utilizzo di StringBuilder / StringBuffer

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

String result = sb.toString();

if (result.equals("user_123")) {
    // comparison
}

Utilizzo di caching o mappe per ridurre i confronti

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

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

6. FAQ

D1. Qual è la differenza tra == e equals()?

R.

  • == confronta i riferimenti
  • equals() confronta il contenuto della stringa

D2. Perché equals() genera un errore quando la variabile è null?

String input = null;
input.equals("test"); // Exception

Utilizzare un confronto con costante in prima posizione:

"test".equals(input);

D3. Come confronto le stringhe ignorando il caso?

stringA.equalsIgnoreCase(stringB);

D4. Come confronto l’ordine alfabetico?

a.compareTo(b);

7. Conclusione

Scegliere correttamente il metodo di confronto giusto è importante

Poiché String è un tipo di riferimento, un confronto errato porta spesso a comportamenti inaspettati. Comprendere == vs equals() è essenziale.

Checklist

  • == : confronta i riferimenti
  • equals() : confronta il contenuto
  • equalsIgnoreCase() : ignora il caso
  • compareTo() : ordine lessicografico
  • "constant".equals(variable) : sicuro per null
  • Utilizzare intern() o caching per confronti intensivi

Conoscenza pratica ed essenziale

Il confronto di stringhe appare nei controlli di login, validazione, operazioni sul database, logica di ramificazione e molte attività quotidiane. Conoscere le tecniche corrette aiuta a scrivere codice più sicuro e affidabile. Utilizzare questa guida come riferimento quando si lavora con stringhe in Java.