Perulangan #
Perulangan (loop) adalah mekanisme untuk mengeksekusi blok kode berulang kali selama kondisi tertentu terpenuhi. Java menyediakan empat konstruksi perulangan — for, while, do-while, dan enhanced for — masing-masing dengan konteks penggunaan yang berbeda. Pilihan konstruksi yang tepat bukan hanya soal selera, tetapi juga soal keterbacaan: for mengkomunikasikan “aku tahu berapa kali iterasi”, while mengkomunikasikan “aku iterasi sampai kondisi berubah”, dan for-each mengkomunikasikan “aku tidak peduli indeks, hanya elemennya”. Artikel ini membahas semua konstruksi beserta pola break/continue, labeled loop untuk nested loop, dan anti-pattern yang sering menyebabkan bug seperti ConcurrentModificationException.
for Loop #
for adalah pilihan ketika jumlah iterasi sudah diketahui — baik dari angka tetap maupun panjang array/koleksi. Tiga bagian dalam header for adalah: inisialisasi, kondisi, dan update.
// Struktur dasar
for (inisialisasi; kondisi; update) {
// kode diulang selama kondisi true
}
// Iterasi maju
for (int i = 0; i < 5; i++) {
System.out.println("i = " + i); // 0, 1, 2, 3, 4
}
// Iterasi mundur
for (int i = 10; i > 0; i--) {
System.out.println(i); // 10, 9, ..., 1
}
// Langkah dua
for (int i = 0; i <= 20; i += 2) {
System.out.println(i); // 0, 2, 4, ..., 20
}
flowchart TD
A([Mulai]) --> B["Inisialisasi\nint i = 0"]
B --> C{"Kondisi?\ni < 5"}
C -- false --> G([Selesai])
C -- true --> D["Eksekusi\nbody loop"]
D --> E["Update\ni++"]
E --> CVariasi for Loop #
// Beberapa variabel kontrol
for (int i = 0, j = 10; i < j; i++, j--) {
System.out.println("i=" + i + " j=" + j);
}
// Kondisi kosong = infinite loop
for (;;) {
// eksekusi terus sampai ada break
if (kondisiKeluar) break;
}
// Iterasi array dengan indeks
String[] buah = {"apel", "pisang", "jeruk"};
for (int i = 0; i < buah.length; i++) {
System.out.println(i + ": " + buah[i]);
}
// Iterasi mundur pada array
for (int i = buah.length - 1; i >= 0; i--) {
System.out.println(buah[i]);
}
while Loop #
while adalah pilihan ketika jumlah iterasi tidak diketahui di awal — loop berjalan selama kondisi masih true. Kondisi dievaluasi sebelum setiap iterasi, sehingga body mungkin tidak pernah dieksekusi jika kondisi langsung false.
// Struktur dasar — kondisi diperiksa dulu
while (kondisi) {
// kode dieksekusi selama kondisi true
}
// Contoh: baca input sampai valid
Scanner scanner = new Scanner(System.in);
int input = -1;
while (input < 1 || input > 10) {
System.out.print("Masukkan angka 1-10: ");
input = scanner.nextInt();
}
// Contoh: proses data dari stream
BufferedReader reader = new BufferedReader(new FileReader("data.txt"));
String baris;
while ((baris = reader.readLine()) != null) {
proses(baris);
}
// Contoh: algoritma pencarian konvergensi
double estimasi = 1.0;
double target = 2.0;
while (Math.abs(estimasi * estimasi - target) > 1e-10) {
estimasi = (estimasi + target / estimasi) / 2.0;
}
Infinite loop terjadi ketika kondisi tidak pernah menjadi
false. Pastikan ada sesuatu di dalam loop yang akhirnya membuat kondisifalse:// ANTI-PATTERN: kondisi tidak pernah false — loop selamanya int i = 0; while (i < 10) { System.out.println(i); // lupa i++! } // BENAR int i = 0; while (i < 10) { System.out.println(i); i++; // kondisi akhirnya menjadi false }
do-while Loop #
do-while mengeksekusi body dulu sebelum memeriksa kondisi. Ini menjamin body dieksekusi minimal satu kali — berguna untuk menu interaktif, validasi input pertama kali, dan situasi di mana satu eksekusi awal selalu diperlukan.
// Kondisi diperiksa SETELAH body dieksekusi
do {
// kode ini pasti dieksekusi minimal sekali
} while (kondisi);
// Contoh klasik: menu interaktif
Scanner sc = new Scanner(System.in);
int pilihan;
do {
System.out.println("1. Lihat data");
System.out.println("2. Tambah data");
System.out.println("0. Keluar");
System.out.print("Pilihan: ");
pilihan = sc.nextInt();
prosesMenu(pilihan);
} while (pilihan != 0);
// Contoh: validasi input — coba dulu, validasi kemudian
String input;
do {
System.out.print("Masukkan nama (min 3 karakter): ");
input = sc.next();
} while (input.length() < 3);
flowchart TD
A([Mulai]) --> B["Eksekusi\nbody loop"]
B --> C{"Kondisi?\npilihan != 0"}
C -- true --> B
C -- false --> D([Selesai])Perbedaan while vs do-while #
| Aspek | while | do-while |
|---|---|---|
| Kapan kondisi diperiksa | Sebelum setiap iterasi | Setelah setiap iterasi |
| Minimal eksekusi body | 0 kali (jika kondisi awal false) | 1 kali (selalu) |
| Penggunaan tipikal | Kondisi tidak diketahui, mungkin 0 iterasi | Input/menu, kondisi awal selalu butuh satu eksekusi |
Enhanced for (for-each) #
Enhanced for atau for-each adalah cara paling bersih untuk mengiterasi semua elemen array atau koleksi yang mengimplementasikan Iterable. Tidak perlu indeks, tidak perlu get(i) — cukup nama elemen.
// Array
int[] angka = {10, 20, 30, 40, 50};
for (int n : angka) {
System.out.println(n);
}
// List
List<String> nama = List.of("Andi", "Budi", "Cici");
for (String s : nama) {
System.out.println(s.toUpperCase());
}
// Set — urutan tidak dijamin
Set<String> kota = Set.of("Jakarta", "Surabaya", "Bandung");
for (String k : kota) {
System.out.println(k);
}
// Map — iterasi entri
Map<String, Integer> nilai = Map.of("Andi", 85, "Budi", 90);
for (Map.Entry<String, Integer> entri : nilai.entrySet()) {
System.out.println(entri.getKey() + ": " + entri.getValue());
}
Keterbatasan for-each #
List<String> daftar = new ArrayList<>(List.of("a", "b", "c", "d"));
// ANTI-PATTERN 1: tidak bisa modifikasi koleksi saat iterasi
for (String s : daftar) {
if (s.equals("b")) {
daftar.remove(s); // ✗ ConcurrentModificationException!
}
}
// BENAR: gunakan Iterator.remove() untuk hapus saat iterasi
Iterator<String> it = daftar.iterator();
while (it.hasNext()) {
if (it.next().equals("b")) {
it.remove(); // ✓ aman
}
}
// ATAU gunakan removeIf() — cara paling ringkas
daftar.removeIf(s -> s.equals("b")); // ✓
// ANTI-PATTERN 2: tidak bisa akses indeks dalam for-each
for (String s : daftar) {
// tidak ada cara tahu indeks s tanpa variabel counter terpisah
}
// BENAR: gunakan for biasa jika butuh indeks
for (int i = 0; i < daftar.size(); i++) {
System.out.println(i + ": " + daftar.get(i));
}
break dan continue #
break menghentikan loop sepenuhnya, continue melompat ke iterasi berikutnya. Keduanya bekerja di semua jenis loop.
// break — keluar dari loop
for (int i = 0; i < 10; i++) {
if (i == 5) break; // loop berhenti saat i = 5
System.out.println(i); // cetak 0, 1, 2, 3, 4
}
// continue — lewati iterasi ini, lanjut ke berikutnya
for (int i = 0; i < 10; i++) {
if (i % 2 == 0) continue; // lewati angka genap
System.out.println(i); // cetak 1, 3, 5, 7, 9
}
// break dalam while — pola "loop-and-a-half"
while (true) {
String input = scanner.next();
if (input.equals("quit")) break;
proses(input);
}
Labeled break dan continue #
Ketika ada nested loop, break dan continue biasa hanya mempengaruhi loop terdalam. Gunakan label untuk mengontrol loop yang lebih luar:
// Tanpa label — break hanya keluar dari loop dalam
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (j == 1) break; // hanya keluar dari loop j
}
System.out.println("i masih berjalan: " + i);
}
// Dengan label — break keluar dari loop luar sekaligus
luarLoop:
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (i == 1 && j == 1) break luarLoop; // keluar dari kedua loop
System.out.println("i=" + i + " j=" + j);
}
}
// Labeled continue — lewati iterasi loop luar
luarLoop:
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (j == 1) continue luarLoop; // lanjut ke iterasi i berikutnya
System.out.println("i=" + i + " j=" + j);
}
}
flowchart TD
subgraph "Nested Loop dengan Label"
A["Loop luar: i=0..2"] --> B["Loop dalam: j=0..2"]
B --> C{Kondisi\nbreak/continue?}
C -- "break (tanpa label)" --> D["Keluar loop dalam\nLoop luar lanjut"]
C -- "break luarLoop" --> E["Keluar KEDUA loop"]
C -- "continue (tanpa label)" --> F["Iterasi j berikutnya"]
C -- "continue luarLoop" --> G["Iterasi i berikutnya"]
C -- Tidak --> H["Eksekusi body"]
endInfinite Loop yang Disengaja #
Beberapa pola pemrograman memang butuh loop yang berjalan terus — seperti server yang menunggu koneksi, atau game loop. Gunakan while (true) atau for (;;) dan kendalikan dengan break:
// Server loop — berjalan sampai shutdown
while (true) {
try {
Socket klien = serverSocket.accept();
tanganiKlien(klien);
} catch (SocketException e) {
System.out.println("Server dihentikan");
break;
}
}
// Polling dengan timeout
long batas = System.currentTimeMillis() + 5000; // 5 detik
while (true) {
if (dataReady()) break;
if (System.currentTimeMillis() > batas) {
throw new TimeoutException("Timeout menunggu data");
}
Thread.sleep(100); // tunggu 100ms sebelum cek lagi
}
Memilih Konstruksi Perulangan #
| Situasi | Pilihan |
|---|---|
| Jumlah iterasi diketahui (0 sampai N) | for |
| Iterasi array/koleksi, tidak butuh indeks | for-each |
| Butuh indeks atau iterasi mundur | for dengan indeks |
| Iterasi sampai kondisi berubah, mungkin 0 kali | while |
| Harus eksekusi minimal 1 kali (menu, validasi) | do-while |
| Modifikasi koleksi saat iterasi | Iterator atau removeIf() |
| Transformasi/filter koleksi modern | Stream API |
flowchart TD
A{Iterasi apa?} --> B[Array atau Collection]
A --> C[Kondisi tidak diketahui]
A --> D[Jumlah pasti diketahui]
B --> E{Butuh indeks\natau modifikasi?}
E -- Tidak --> F["for-each\nfor String s : list"]
E -- Ya --> G["for biasa\natau Iterator"]
C --> H{Harus jalan\nminimal sekali?}
H -- Ya --> I["do-while"]
H -- Tidak --> J["while"]
D --> K["for\nfor int i = 0; i < n; i++"]
style F fill:#16a34a,color:#fff
style I fill:#3b82f6,color:#fff
style J fill:#3b82f6,color:#fff
style K fill:#16a34a,color:#fffRingkasan #
foruntuk jumlah iterasi diketahui — tiga bagian header (init; kondisi; update) mengkomunikasikan batas loop secara eksplisit; gunakan saat iterasi ke-N sudah diketahui.whileuntuk kondisi tidak diketahui — kondisi diperiksa sebelum iterasi; jika kondisi langsung false, body tidak pernah dieksekusi.do-whileuntuk minimal satu eksekusi — kondisi diperiksa setelah body; cocok untuk menu interaktif dan validasi input yang butuh satu percobaan dulu.for-eachuntuk iterasi elemen — paling bersih untuk array danIterable; tidak bisa digunakan jika butuh indeks, iterasi mundur, atau modifikasi koleksi saat iterasi.ConcurrentModificationException— jangan hapus atau tambah elemen dari koleksi langsung dalamfor-each; gunakaniterator.remove()atauremoveIf()sebagai gantinya.- Labeled break/continue untuk nested loop — tanpa label,
breakhanya keluar dari loop terdalam; beri label pada loop luar untuk mengontrol loop mana yang dipengaruhi.- Infinite loop disengaja pakai
while (true)— pola ini valid untuk server loop dan polling; selalu sertakan kondisibreakyang jelas dan mekanisme timeout.- Prefer for-each dan Stream API — untuk kode modern,
for-eachlebih aman dan lebih ringkas; untuk transformasi/filter koleksi kompleks, pertimbangkan Stream API (stream().filter().map().collect()).