Confronto delle stringhe in Java: guida completa alle differenze e all’uso di ==, equals e compareTo

目次

1. Introduzione

Qual è l’importanza del confronto delle stringhe in Java?

Nella programmazione Java, le occasioni in cui si gestiscono stringhe (String) sono numerose. Controlli di nomi utente, corrispondenza di valori di input da form, verifica di risposte API e così via: in ogni situazione è richiesto il confronto di stringhe.

In questi casi, “come confrontare correttamente le stringhe” è un punto in cui i principianti tendono a inciampare in modo inaspettato. In particolare, se non si comprende la differenza tra l’operatore == e il metodo equals(), può causare bug con risultati non intenzionali.

È pericoloso non capire la differenza tra “==” e “equals”

Ad esempio, guardate il seguente codice.

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

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

Molti rimarranno sorpresi dal risultato di output di questo codice. Anche se sono la stessa stringa, == restituisce false, mentre equals() restituisce true. Questo accade perché Java tratta le stringhe come “tipo di riferimento”, e == confronta gli indirizzi di riferimento.

In questo modo, confrontare correttamente le stringhe è direttamente collegato all’affidabilità e alla leggibilità del programma. Al contrario, comprendere il metodo corretto permette di prevenire bug in anticipo.

Cosa si può imparare in questo articolo

In questo articolo, spiegheremo in dettaglio i metodi di confronto delle stringhe in Java, dalla base all’avanzato. Risponderemo a domande come le seguenti, spiegando in modo strutturato e comprensibile anche per i principianti.

  • Qual è la differenza tra == e equals()?
  • Come confrontare ignorando maiuscole e minuscole?
  • Come confrontare le stringhe in ordine lessicografico?
  • Come evitare eccezioni nel confronto con null?

Attraverso esempi di codice utili nella pratica, acquisiremo solidamente la conoscenza corretta del confronto delle stringhe.

2. Le basi delle stringhe in Java

Le stringhe sono un “tipo di riferimento”

In Java, il tipo String non è un tipo primitivo (come int o boolean), ma un “tipo di riferimento (Reference Type)”. Questo significa che la variabile String non contiene i dati della stringa effettivi, ma riferisce a un oggetto stringa esistente nella memoria heap.

Quindi, se si scrive in questo modo:

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

a e b riferiscono alla stessa stringa "hello", quindi a == b potrebbe restituire true. Tuttavia, questo è dovuto al meccanismo di ottimizzazione dei letterali stringa (String Interning) di Java.

La differenza tra letterali stringa e new String()

In Java, se si utilizza lo stesso letterale stringa più volte, vengono ottimizzati come lo stesso riferimento. Questo è una funzionalità di Java per condividere le stringhe a runtime e migliorare l’efficienza della memoria.

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

D’altra parte, utilizzando la parola chiave new per generare esplicitamente un oggetto, si crea un nuovo riferimento.

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

In questo modo, == verifica la corrispondenza del riferimento, mentre equals() verifica la corrispondenza del contenuto, quindi i loro usi sono molto diversi.

String è una classe “immutabile”

Un’altra caratteristica importante è che String è immutabile (immutable). Cioè, il contenuto di un oggetto String una volta creato non può essere modificato.

Ad esempio, se si scrive in questo modo:

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

Sembra che si stia aggiungendo una stringa all’originale original, ma in realtà viene generato un nuovo oggetto String e assegnato a original.

Grazie a questa immutabilità, String è thread-safe e utile per la sicurezza e l’ottimizzazione della cache.

3. Metodi di confronto delle stringhe

Confronto di riferimenti con l’operatore ==

== confronta i riferimenti (indirizzi) degli oggetti stringa. Quindi, anche se il contenuto è lo stesso, se sono oggetti diversi, restituisce false.

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

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

In questo esempio, a è un letterale e b è generato con new, quindi i riferimenti sono diversi e restituisce false. Non è adatto per il confronto del contenuto, quindi fate attenzione.

Confronto del contenuto con il metodo equals()

equals() è il metodo corretto per confrontare il contenuto delle stringhe. È raccomandato utilizzarlo in molte situazioni.

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

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

In questo modo, anche se i riferimenti sono diversi, se il contenuto è lo stesso, restituisce true.

Punti di attenzione nel confronto con null

Il seguente codice può causare NullPointerException.

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

Per evitarlo, è raccomandato scrivere nel formato costante.equals(variabile).

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

Confronto ignorando maiuscole e minuscole con il metodo equalsIgnoreCase()

Per nomi utente o indirizzi email, dove non si vuole distinguere tra maiuscole e minuscole, equalsIgnoreCase() è comodo.

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

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

Tuttavia, in alcuni casi speciali Unicode (come la “İ” in turco), potrebbe comportarsi in modo inaspettato, quindi per l’internazionalizzazione sono necessarie considerazioni aggiuntive.

Confronto lessicografico con il metodo compareTo()

compareTo() confronta due stringhe in ordine lessicografico e restituisce un intero come segue.

  • 0: uguali
  • Valore negativo: la stringa del chiamante è prima (più piccola)
  • Valore positivo: la stringa del chiamante è dopo (più grande)
    String a = "apple";
    String b = "banana";
    
    System.out.println(a.compareTo(b)); // 負の値("apple"は"banana"より前)
    

È spesso utilizzato per l’ordinamento lessicografico o il filtraggio, e internamente in Collections.sort() o nel confronto delle chiavi di TreeMap.

4. Esempi pratici di utilizzo

Verifica dell’input utente (funzione di login)

Una delle situazioni più comuni è la verifica della corrispondenza di nomi utente o password.

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

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

.Come mostrato in questo esempio, quando si desidera confrontare ignorando maiuscole e minuscole è opportuno usare equalsIgnoreCase().

Tuttavia, per motivi di sicurezza, i confronti di password dovrebbero distinguere tra maiuscole e minuscole, quindi usate equals().

Validazione dell’input (elaborazione dei form)

Ad esempio, il controllo dei valori inseriti da menu a discesa o caselle di testo utilizza il confronto di stringhe.

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

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

In questo modo, come confronto sicuro che include il controllo null, la forma "costante".equals(variazione) è comunemente usata in pratica. Poiché l’input dell’utente non è garantito, questo modo di scrivere previene NullPointerException.

Gestione di più condizioni (uso simile a switch)

Quando si desidera gestire più possibili stringhe in un ramo condizionale, è comune usare equals() in sequenza.

String cmd = args[0];

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

A partire da Java 14, è possibile utilizzare ufficialmente la dichiarazione switch con le stringhe.

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

Come si vede, il confronto di stringhe è direttamente collegato alla logica di ramificazione, quindi è necessaria una comprensione accurata.

Bug causati dal confronto con null e relative contromisure

Un errore comune è che l’applicazione si blocca quando si confronta con un valore null.

String keyword = null;

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

In questi casi, è possibile confrontare in modo sicuro scrivendo come segue.

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

Oppure, è possibile effettuare prima un controllo null più rigoroso.

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

Il codice sicuro rispetto a null è una competenza indispensabile per aumentare la robustezza.

5. Prestazioni e ottimizzazione

Costo di elaborazione nel confronto di stringhe

equals() e compareTo() sono generalmente ottimizzati per funzionare velocemente, ma poiché confrontano carattere per carattere, possono avere un impatto quando si gestiscono stringhe lunghe o grandi quantità di dati. In particolare, confrontare ripetutamente la stessa stringa all’interno di un ciclo può causare un degrado delle prestazioni non previsto.

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

Accelerare il confronto con String.intern()

Utilizzando il metodo String.intern() di Java, è possibile registrare le stringhe con lo stesso contenuto nel “pool di stringhe” della JVM e condividere i riferimenti. Questo permette anche di confrontare con ==, offrendo potenziali vantaggi di prestazioni.

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

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

Tuttavia, un uso eccessivo del pool di stringhe può comprimere l’area heap, quindi dovrebbe essere limitato a casi d’uso specifici.

Trappole di equalsIgnoreCase() e alternative

equalsIgnoreCase() è comodo, ma poiché converte maiuscole e minuscole durante il confronto, può avere un costo leggermente superiore rispetto a equals() standard. In scenari con requisiti di prestazioni severi, è più veloce confrontare valori già uniformati in maiuscolo o minuscolo.

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

Come mostrato, convertire in anticipo e poi usare equals() migliora l’efficienza del confronto.

Utilizzo di StringBuilder / StringBuffer

Nei casi in cui si concatenano molte stringhe, l’uso di String genera un nuovo oggetto ad ogni operazione, aumentando il carico su memoria e CPU. Anche quando il confronto è coinvolto, la migliore pratica è usare StringBuilder per la concatenazione e la costruzione, mantenendo le stringhe per il confronto come String.

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

String result = sb.toString();

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

Progettazione per velocizzare con cache e pre-elaborazione

Quando lo stesso confronto di stringa avviene più volte, è efficace memorizzare il risultato del confronto in cache o pre-elaborare usando una mappa (come HashMap) per ridurre il numero di 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();
}

In questo modo, il confronto di stringhe con equals() viene sostituito da una singola ricerca nella mappa, migliorando sia la leggibilità che le prestazioni.

6. Domande frequenti (FAQ)

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

A. == esegue un confronto di riferimento (cioè verifica se gli indirizzi di memoria coincidono). equals(), invece, confronta il contenuto della stringa.

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

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

Quindi, quando si desidera confrontare il contenuto di una stringa, usate sempre equals().

Q2. Perché l’uso di equals() può generare errori a causa di null?

A. Chiamare un metodo su null genera una NullPointerException.

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

Per evitare questo errore, è sicuro scrivere il confronto partendo dalla costante, come segue.

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

Q3. Come confrontare ignorando maiuscole e minuscole?

.A.
Usando il metodo equalsIgnoreCase() è possibile confrontare ignorando le differenze tra maiuscole e minuscole.

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

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

Tuttavia, con caratteri a larghezza intera o alcuni caratteri Unicode speciali, il risultato può essere inatteso, quindi è necessario fare attenzione.

Q4. Se vuoi confrontare le stringhe in base all’ordine di ordinamento?

A.
Quando vuoi verificare la relazione di ordine lessicografico di una stringa, usa compareTo().

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

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

Significato del valore di ritorno:

  • 0 → uguale
  • valore negativo → la stringa a sinistra è prima
  • valore positivo → la stringa a sinistra è dopo

Viene utilizzato in operazioni di ordinamento, ecc.

Q5. Quali sono le best practice da ricordare per il confronto di stringhe?

A.

  • Confronto del contenuto: usa sempre equals()
  • Sicurezza null: usa la forma "costante".equals(variaibile)
  • Ignorare maiuscole/minuscole: usa equalsIgnoreCase() o converti prima con toLowerCase() / toUpperCase()
  • Situazioni con molti confronti o necessità di velocità: considera intern() o una progettazione di cache
  • Equilibrio tra leggibilità e sicurezza: tienilo sempre presente

7. Riepilogo

In Java, è importante “usare correttamente” i confronti di stringhe

In questo articolo abbiamo spiegato in modo completo le basi, le pratiche e le considerazioni di performance del confronto di stringhe in Java. Poiché String è un tipo di riferimento, un uso errato del metodo di confronto può portare a comportamenti inattesi.

In particolare, la differenza tra == e equals() è un punto che confonde spesso i principianti. Capirla correttamente e usarla in modo appropriato è fondamentale per scrivere codice sicuro e affidabile.

Cosa hai imparato da questo articolo (checklist)

  • == è un operatore che confronta i riferimenti (indirizzi di memoria)
  • equals() è il metodo che confronta il contenuto della stringa (il più sicuro)
  • Usando equalsIgnoreCase() è possibile ignorare maiuscole/minuscole
  • Con compareTo() è possibile confrontare l’ordine lessicografico delle stringhe
  • Con l’ordine "costante".equals(variaibile) è possibile confrontare in modo null‑safe
  • Se si considera la performance, intern() o una progettazione di cache sono utili

Conoscenze sul confronto di stringhe utili anche in ambito professionale

Il confronto di stringhe è un elemento strettamente legato alle attività di sviluppo quotidiane, come la verifica di login, la validazione degli input, le ricerche nel database e le condizioni di flusso. Conoscere questi aspetti permette di prevenire bug e garantire che il codice si comporti come previsto.

In futuro, quando scriverai codice che gestisce stringhe, ti consigliamo di fare riferimento a questo articolo e scegliere il metodo di confronto più adatto allo scopo.