Quando non hai un contatore
Il ciclo for è costruito attorno a un contatore che imposti all'inizio. Ma molti cicli non hanno un conteggio chiaro: continuare a leggere righe finché il file non finisce, continuare a chiedere una password finché non è corretta, continuare a dimezzare un numero finché non arriva a 1. In questi casi il ciclo while è la scelta naturale: si ripete semplicemente finché una condizione resta vera.
Un ciclo while controlla la condizione prima di ogni passaggio, compreso il primo. Se la condizione è falsa fin dall'inizio, il corpo non viene mai eseguito.
La condizione count > 0 viene controllata per prima; se è vera, il corpo viene eseguito; poi si torna all'inizio e si controlla di nuovo. La riga count-- è ciò che alla fine rende falsa la condizione: toglila e il ciclo gira per sempre.
Anatomia di un ciclo while
Confrontalo con il ciclo for in tre parti che già conosci. Un ciclo while separa questi tre compiti: l'inizializzazione la fai prima del ciclo, la condizione va tra parentesi e l'aggiornamento sta dentro il corpo.
int i = 0; // inizializzazione: prima del ciclo
while (i < 5) { // condizione: controllata a ogni passaggio
System.out.println(i);
i++; // aggiornamento: devi ricordartelo tu
}
Quest'ultima parte è il punto debole. In un ciclo for l'aggiornamento sta proprio accanto alla condizione, quindi è difficile dimenticarlo. In un ciclo while è solo un'altra istruzione del corpo: l'errore più comune in assoluto è ometterlo e bloccare il programma.
do-while: eseguire il corpo almeno una volta
A volte serve che il corpo venga eseguito una volta prima ancora di poter decidere se ripetere. Il ciclo do-while controlla la condizione in fondo, quindi il corpo viene sempre eseguito almeno una volta:
Devi chiedere e leggere almeno una volta prima di sapere se l'input è valido, ed è esattamente ciò che ti offre do-while. Nota il punto e virgola dopo while (...): è obbligatorio, e dimenticarlo è un errore di compilazione frequente.
La differenza rispetto a un while normale si vede chiaramente quando la condizione è falsa fin dall'inizio:
Viene stampato solo do-while body. Il ciclo while salta completamente il corpo perché x < 5 era falso già prima del primo controllo.
Ciclare fino a un valore sentinella
Un uso classico di while è leggere fino a un valore speciale di "stop": una sentinella. Qui non c'è alcun contatore; vai avanti finché i dati non ti dicono di fermarti:
Lo schema è leggere una volta prima del ciclo, poi leggere di nuovo in fondo a ogni passaggio. In questo modo la condizione verifica sempre un input nuovo. Se dimentichi la seconda lettura dentro il corpo, value non cambia mai e ottieni un ciclo infinito.
break e continue nei cicli while
break e continue funzionano qui come in un ciclo for. break esce subito dal ciclo; continue torna direttamente al controllo della condizione, saltando il resto del passaggio corrente:
Questo stampa 1 3 5 7 9. Il while (true) volutamente non si ferma mai da solo: il break è l'unica uscita. Fai attenzione a continue in un ciclo while: dato che l'aggiornamento fa parte del corpo, tornare all'inizio con continue prima di aver fatto avanzare il contatore è un modo silenzioso per bloccare il ciclo. Nell'esempio sopra n++ viene eseguito per primo, quindi è sicuro.
Attenzione ai cicli infiniti
La condizione deve prima o poi diventare falsa, e questo dipende interamente da qualcosa che cambia dentro il corpo. I due colpevoli tipici sono dimenticare l'aggiornamento e aggiornare nel modo sbagliato:
int i = 0;
while (i < 5) {
System.out.println(i); // i non cambia mai -> gira per sempre
}
int i = 0;
while (i != 10) {
i += 3; // 0, 3, 6, 9, 12... salta oltre il 10 -> gira per sempre
}
Il primo si blocca perché i non viene mai aggiornato. Il secondo si blocca perché il contatore scavalca il valore esatto che la condizione cerca: preferisci < o <= a != quando il passo potrebbe non cadere esattamente sul valore. Un while (true) va bene se ha un break garantito; uno accidentale è solo un bug.
Prossimo passo: il ciclo for-each
Un ciclo while è lo strumento giusto quando cicli finché cambia una condizione e non c'è un conteggio chiaro. Ma quando vuoi semplicemente visitare ogni elemento di un array o di una lista (senza indice, senza condizione da gestire) esiste qualcosa di ancora più pulito: il ciclo for-each migliorato. È l'argomento della prossima pagina.
Domande frequenti
Qual è la differenza tra while e do-while in Java?
Un ciclo while controlla la condizione prima del primo passaggio, quindi il corpo può essere eseguito zero volte. Un ciclo do-while esegue il corpo una volta per primo, poi controlla la condizione in fondo: per questo viene sempre eseguito almeno una volta. Usa do-while quando il lavoro va fatto prima di poter decidere se ripetere (come chiedere un input e poi validarlo).
Quando conviene usare un ciclo while invece di un for in Java?
Usa un ciclo while quando non hai un contatore chiaro e vuoi solo ripetere finché una condizione non cambia: leggere input finché l'utente non scrive quit, controllare finché un valore non è pronto o elaborare una coda finché non si svuota. Scegli un ciclo for quando conosci il numero di iterazioni o hai un indice ovvio da contare.
Come si ferma un ciclo while infinito in Java?
Assicurati che qualcosa nel corpo finisca per rendere falsa la condizione (decrementare un contatore, far avanzare un puntatore, impostare un flag). Per un ciclo while (true) voluto, metti all'interno un break protetto da un if. Se un ciclo si blocca, la causa tipica è aver dimenticato di aggiornare la variabile da cui dipende la condizione.