Perulangan

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 --> C

Variasi 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 kondisi false:

// 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 #

Aspekwhiledo-while
Kapan kondisi diperiksaSebelum setiap iterasiSetelah setiap iterasi
Minimal eksekusi body0 kali (jika kondisi awal false)1 kali (selalu)
Penggunaan tipikalKondisi tidak diketahui, mungkin 0 iterasiInput/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"]
    end

Infinite 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 #

SituasiPilihan
Jumlah iterasi diketahui (0 sampai N)for
Iterasi array/koleksi, tidak butuh indeksfor-each
Butuh indeks atau iterasi mundurfor dengan indeks
Iterasi sampai kondisi berubah, mungkin 0 kaliwhile
Harus eksekusi minimal 1 kali (menu, validasi)do-while
Modifikasi koleksi saat iterasiIterator atau removeIf()
Transformasi/filter koleksi modernStream 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:#fff

Ringkasan #

  • for untuk jumlah iterasi diketahui — tiga bagian header (init; kondisi; update) mengkomunikasikan batas loop secara eksplisit; gunakan saat iterasi ke-N sudah diketahui.
  • while untuk kondisi tidak diketahui — kondisi diperiksa sebelum iterasi; jika kondisi langsung false, body tidak pernah dieksekusi.
  • do-while untuk minimal satu eksekusi — kondisi diperiksa setelah body; cocok untuk menu interaktif dan validasi input yang butuh satu percobaan dulu.
  • for-each untuk iterasi elemen — paling bersih untuk array dan Iterable; tidak bisa digunakan jika butuh indeks, iterasi mundur, atau modifikasi koleksi saat iterasi.
  • ConcurrentModificationException — jangan hapus atau tambah elemen dari koleksi langsung dalam for-each; gunakan iterator.remove() atau removeIf() sebagai gantinya.
  • Labeled break/continue untuk nested loop — tanpa label, break hanya 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 kondisi break yang jelas dan mekanisme timeout.
  • Prefer for-each dan Stream API — untuk kode modern, for-each lebih aman dan lebih ringkas; untuk transformasi/filter koleksi kompleks, pertimbangkan Stream API (stream().filter().map().collect()).

← Sebelumnya: Seleksi Kondisi   Berikutnya: Fungsi →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact