Operatori di confronto Java spiegati: ==, !=, <, > e differenze di equals()

目次

1. Cosa Imparerai in Questo Articolo (Punti Chiave Prima)

In Java, gli operatori di confronto sono funzionalità fondamentali del linguaggio usate per confrontare valori come numeri e caratteri.
Tuttavia, molti principianti hanno difficoltà quando confrontano oggetti come String o Integer, soprattutto usando in modo errato l’operatore ==.

Questa sezione riassume i punti chiave in anticipo, così potrai capire rapidamente quando gli operatori di confronto sono sicuri da usare—e quando non lo sono.

1.1 Gli operatori di confronto rientrano in due categorie

Gli operatori di confronto di Java possono essere raggruppati in due tipi principali:

  • Operatori relazionali (confronto d’ordine) < , <= , > , >=
  • Operatori di uguaglianza (confronto di uguaglianza) == , !=

Quando si tratta di tipi primitivi (come int, double o char), questi operatori si comportano esattamente come ti aspetti.

int a = 10;
int b = 20;

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

1.2 Importante: == NON confronta sempre i valori

Questa è la fonte di confusione più comune in Java.

  • Per tipi primitivi, == confronta i valori effettivi
  • Per tipi di riferimento (oggetti), == confronta se entrambe le variabili puntano allo stesso oggetto

Questa differenza è fondamentale.

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

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

Anche se il testo sembra identico, s1 e s2 sono oggetti diversi in memoria.

👉 Regola pratica:
Se vuoi verificare se due oggetti hanno lo stesso contenuto, usa equals().

1.3 Guida rapida alla decisione (Cheat Sheet)

Se ricordi solo una cosa, ricordati questa:

  • Valori primitivi (int, boolean, char, ecc.) → Usa gli operatori di confronto (==, <, >, ecc.)
  • Confronto del contenuto degli oggetti (String, Integer, oggetti personalizzati) → Usa equals() o Objects.equals()
  • Ordinamento o confronto di grandezza (qual è più grande/minore) → Usa compareTo() o un Comparator

Insight chiave:
== può sembrare semplice, ma per gli oggetti risponde a
“Sono lo stesso oggetto?”, non a
“Hanno lo stesso valore?”

1.4 Cosa sarai in grado di fare dopo aver letto questo articolo

Alla fine di questo articolo, sarai in grado di:

  • Usare gli operatori di confronto di Java correttamente e in sicurezza
  • Evitare bug comuni causati da == vs equals()
  • Comprendere perché i confronti di Integer a volte si comportano in modo incoerente
  • Scegliere l’approccio giusto quando confronti valori, oggetti o ordine

Nella sezione successiva, inizieremo con una panoramica completa di tutti gli operatori di confronto di Java, prima di approfondire le insidie comuni e le migliori pratiche.

2. Operator​i di Confronto Java (Elenco Completo)

In questa sezione, organizzeremo tutti gli operatori di confronto di Java e chiariremo cosa possono e cosa non possono confrontare.
L’obiettivo è eliminare ogni ambiguità prima di addentrarci in casi più complessi.

2.1 Gli operatori di confronto restituiscono sempre un booleano

Ogni operatore di confronto in Java restituisce un valore di tipo boolean:

  • true → la condizione è soddisfatta
  • false → la condizione non è soddisfatta

Per questo gli operatori di confronto sono comunemente usati in if, while e altre istruzioni di controllo.

int x = 5;
int y = 10;

boolean result = x < y;  // true

Gli operatori di confronto non modificano i valori—valutano solo le condizioni.

2.2 Operator​i relazionali: <, <=, >, >=

Questi operatori confrontano ordine o grandezza.
Sono usati principalmente con tipi numerici e char.

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

Esempio con numeri

int a = 10;
int b = 20;

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

Esempio con char

I caratteri sono confrontati usando i loro valori Unicode.

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

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

Nota:
Questi operatori non possono essere usati con String. Farlo provoca un errore di compilazione.

2.3 Operatori di uguaglianza: == e !=

Gli operatori di uguaglianza verificano se due operandi sono uguali o meno.

OperatorMeaning
==equal to
!=not equal to

Uso sicuro con tipi primitivi

int x = 5;
int y = 5;

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

Qui, Java confronta valori reali, il che è semplice e sicuro.

2.4 Confrontare i valori boolean

I valori boolean possono anche essere confrontati usando == e !=.

boolean f1 = true;
boolean f2 = false;

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

Nel codice reale, tuttavia, è più leggibile scrivere:

if (isEnabled) {
    // do something
}

invece di:

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

2.5 Tipi che funzionano bene con gli operatori di confronto

Sicuro usare direttamente gli operatori di confronto:

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

Non sicuro o non consentito:

  • String
  • Classi wrapper ( Integer, Long, ecc.)
  • Oggetti personalizzati

Questi tipi richiedono tecniche di confronto diverse, che tratteremo nel prossimo paragrafo.

2.6 Punto chiave di questa sezione

  • Gli operatori di confronto restituiscono sempre true o false
  • Funzionano in modo affidabile con tipi primitivi
  • Usarli con oggetti può portare a bug o errori di compilazione

Nella sezione successiva, ci concentreremo sui tipi primitivi, dove gli operatori di confronto si comportano esattamente come previsto.

3. Operatori di confronto con tipi primitivi (Zona sicura)

I tipi primitivi sono il caso più sicuro e più semplice per gli operatori di confronto.
Comprendere chiaramente questa sezione ti aiuta a riconoscere quando le cose iniziano a complicarsi.

3.1 Cosa sono i tipi primitivi?

I tipi primitivi memorizzano valori reali, non riferimenti.

Esempi comuni includono:

  • Tipi numerici: int, long, double, float
  • Tipo carattere: char
  • Tipo booleano: boolean

Poiché non sono coinvolti riferimenti a oggetti, i confronti si comportano in modo prevedibile.

3.2 Confrontare valori interi e long

int a = 100;
int b = 100;

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

Java confronta direttamente i valori numerici.

Tipi numerici misti

int x = 10;
long y = 10L;

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

Java esegue promozione automatica del tipo prima del confronto.

3.3 Confrontare i caratteri (char)

Sebbene char rappresenti un carattere, Java lo tratta internamente come un numero.

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

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

Questo confronto si basa sui valori Unicode, non sulle regole alfabetiche di una lingua umana.

3.4 Confrontare valori booleani

boolean flag1 = true;
boolean flag2 = false;

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

In pratica, evita confronti ridondanti:

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

3.5 Insidia del confronto a virgola mobile (double / float)

Questa è una trappola classica di Java.

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

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

I numeri a virgola mobile sono memorizzati con limitazioni di precisione.

Approccio consigliato: usare una tolleranza (epsilon)

double epsilon = 0.000001;

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

Per calcoli finanziari o ad alta precisione, considera BigDecimal.

3.6 Riepilogo della zona sicura

  • I tipi primitivi possono essere confrontati direttamente
  • I confronti di char usano i valori Unicode
  • L’uguaglianza a virgola mobile richiede attenzione speciale
  • Fino a questo punto, gli operatori di confronto si comportano in modo intuitivo

Successivamente, passeremo alla zona di pericolo:
perché usare == con gli oggetti porta a risultati inattesi.

4. Perché usare == con gli oggetti causa problemi

Questo è il punto in cui molti principianti Java—e persino gli sviluppatori intermedi—incontrano problemi.
Il comportamento di == cambia una volta che inizi a lavorare con tipi di riferimento (oggetti).

4.1 == confronta i riferimenti agli oggetti, non il contenuto

Per gli oggetti, l’operatore == verifica se entrambe le variabili puntano allo stesso oggetto in memoria.

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

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

Anche se entrambe le stringhe sembrano identiche, sono oggetti diversi, quindi il confronto fallisce.

4.2 equals() confronta il contenuto dell’oggetto

Il metodo equals() è progettato per confrontare l’uguaglianza logica, cioè il contenuto reale degli oggetti.

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

La classe String sovrascrive equals() in modo che confronti le sequenze di caratteri, non gli indirizzi di memoria.

Regola d’oro:

  • Stesso oggetto? → ==
  • Stesso valore/contenuto? → equals()

4.3 Perché == a volte funziona con i letterali di stringa

Questo esempio confonde molti sviluppatori:

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

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

Questo accade a causa del String Pool.

  • I letterali di stringa sono memorizzati in un pool condiviso
  • I letterali identici possono fare riferimento allo stesso oggetto

Tuttavia, questo comportamento è un dettaglio di implementazione, non qualcosa su cui dovresti fare affidamento.

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

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

👉 Usa sempre equals() per il confronto del contenuto delle stringhe.

4.4 Null e equals() — Un altro errore comune

Chiamare equals() su un riferimento null provoca un errore a runtime.

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

Modello sicuro 1: chiamare equals() sulla costante

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

Modello sicuro 2: usare Objects.equals()

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

4.5 Riepilogo di questa sezione

  • == confronta i riferimenti agli oggetti
  • equals() confronta il contenuto
  • Il comportamento del String Pool può nascondere bug
  • Considera sempre la sicurezza dei null

Successivamente, vedremo un altro inganno sottile:
confrontare le classi wrapper come Integer e Long.

5. Confrontare le classi wrapper (Integer, Long, ecc.)

Le classi wrapper sembrano numeri, ma sono comunque oggetti.

5.1 Cosa sono le classi wrapper?

Le classi wrapper consentono ai valori primitivi di essere trattati come oggetti.

PrimitiveWrapper
intInteger
longLong
doubleDouble
booleanBoolean

5.2 Perché == produce risultati incoerenti

Integer a = 100;
Integer b = 100;

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

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

Questo accade a causa della caching di Integer (tipicamente da -128 a 127).

Il risultato di == dipende dal comportamento interno della JVM, non dall’uguaglianza di valore.

5.3 Metodo corretto per confrontare i valori wrapper

Usa equals() per il confronto dei valori.

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

5.4 Insidie di autoboxing e unboxing

Integer a = 100;
int b = 100;

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

Questo funziona grazie al unboxing automatico, ma:

  • Se a è null → NullPointerException
  • L’intento del codice diventa poco chiaro

Un confronto esplicito è più sicuro.

5.5 Modelli di confronto consigliati

  • Confronto di valore → equals() / Objects.equals()
  • Confronto null‑safe → Objects.equals()
  • Confronto di riferimento → == (raro e intenzionale)

5.6 Riepilogo della sezione

  • Le classi wrapper sono tipi di riferimento
  • == è inaffidabile per il confronto di valore
  • Usa equals() in modo coerente

Successivamente, concentriamoci sulle tecniche di confronto null‑safe.

6. Tecniche di confronto null‑safe

I bug legati ai null sono estremamente comuni nelle applicazioni Java.

6.1 Regole di base con null

  • null == null → true
  • Chiamare un metodo su null → errore di runtime
  • Operatori relazionali ( < , > ) con null → errore di compilazione

6.2 Dangerous Pattern

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

6.3 Safe Pattern #1: Constant First

"Java".equals(str);

6.4 Safe Pattern #2: Objects.equals()

Objects.equals(str, "Java");

Questo gestisce tutti i casi null internamente.

6.5 When to Use Objects.equals()

  • Confrontare variabili
  • Valori nullable
  • Logica condizionale più pulita

6.6 Design Tip: Reduce Null Usage

  • Inizializzare i valori presto
  • Usare Optional dove opportuno
  • Meno null → confronti più semplici

6.7 Section Summary

  • Non chiamare mai metodi su riferimenti nullable
  • Preferire utility di confronto null‑safe
  • Progettare per minimizzare l’uso di null

Successivamente, tratteremo confronti di ordinamento usando compareTo().

7. Comparing Order with compareTo()

Gli operatori di confronto non possono determinare l’ordinamento per gli oggetti.

7.1 What Is compareTo()?

compareTo() confronta l’ordine e restituisce:

  • Negativo → minore di
  • Zero → uguale
  • Positivo → maggiore di

7.2 String Ordering Example

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

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

7.3 Wrapper Classes Also Support compareTo()

Integer x = 10;
Integer y = 20;

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

7.4 equals() vs compareTo()

  • Verifica di uguaglianza → equals()
  • Ordinamento/sorting → compareTo()

7.5 Connection to Sorting

Metodi come Collections.sort() si basano su compareTo() internamente.

7.6 Section Summary

  • Gli operatori di confronto non possono confrontare l’ordine degli oggetti
  • compareTo() è lo strumento corretto
  • Essenziale per ordinare e collezioni ordinate

8. Common Mistakes (Quick Checklist)

8.1 Using == with Strings

str1 == str2
str1.equals(str2)

8.2 Using == with Wrapper Classes

Integer a == b
a.equals(b)

8.3 Comparing Floating-Point Values Directly

a == b
✅ usare una tolleranza o BigDecimal

8.4 Forgetting Null Checks

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

8.5 Using < with Objects

str1 < str2
str1.compareTo(str2)

9. Final Summary: How to Choose the Right Comparison

9.1 Decision Guide

  • Tipi primitivi → operatori di confronto
  • Contenuto dell’oggettoequals() / Objects.equals()
  • Ordinamento e sortingcompareTo() / Comparator

9.2 Best Practices

  • Capire cosa significa realmente ==
  • Considerare sempre la sicurezza dei null
  • Evitare di fare affidamento su internals della JVM

9.3 What to Learn Next

  • Operator logici (&& , ||)
  • istruzioni if e switch
  • Comparator per ordinamento personalizzato
  • Implementazione corretta di equals() / hashCode()

FAQ

Q1. What is the difference between == and equals() in Java?

== confronta i riferimenti per gli oggetti, mentre equals() confronta il contenuto.

Q2. Why does == sometimes work with Strings?

A causa del String Pool. Questo comportamento non dovrebbe essere considerato affidabile.

Q3. What is the safest way to compare nullable values?

Usare Objects.equals(a, b).

Q4. How do I compare Strings alphabetically?

Usare compareTo().

Q5. Are comparison operators enough in Java?

Sono sufficienti per i tipi primitivi, ma gli oggetti richiedono equals() e compareTo().