Perulangan

Memilih antara for, while, do-while, dan for-each yang disempurnakan sesuai bentuk masalahnya.

Daftar isi

Perulangan menjalankan satu blok kode berkali-kali selama kondisinya masih terpenuhi, dan Java menyediakan empat bentuknya, yaitu for, while, do-while, serta for yang disempurnakan untuk menelusuri isi array dan koleksi. Topik ini menguraikan cara kerja setiap bentuk, mensimulasikan langkahnya satu per satu, lalu menutup dengan perulangan bersarang. Ketepatan bentuk yang dipilih mengikuti bentuk masalahnya, dari jumlah putaran yang sudah diketahui sampai kondisi yang baru terlihat saat program berjalan.

for dan tiga bagian dalam kurungnya#

Perulangan for mengemas tiga bagian dalam satu baris, yaitu inisialisasi, kondisi, dan pembaruan, masing-masing dipisahkan titik koma. Inisialisasi dijalankan sekali di awal, kondisi diperiksa sebelum setiap putaran, sedangkan pembaruan dieksekusi setelah badan selesai. Variabel penghitung yang dideklarasikan pada bagian inisialisasi hanya hidup di dalam perulangan itu, sehingga tidak menambah lingkup variabel di luarnya.

java TabelPerkalian.java
public class TabelPerkalian {
    public static void main(String[] args) {
        int tabel = 7;
        for (int i = 1; i <= 10; i++) {
            System.out.println(tabel + " x " + i + " = " + (tabel * i));
        }
    }
}
text Keluaran
7 x 1 = 7
7 x 2 = 14
7 x 3 = 21
7 x 4 = 28
7 x 5 = 35
7 x 6 = 42
7 x 7 = 49
7 x 8 = 56
7 x 9 = 63
7 x 10 = 70

Bagian int i = 1 menyiapkan penghitung, kondisi i <= 10 mengizinkan sepuluh putaran, dan i++ menaikkannya setiap badan selesai. Ketiga bagian itu boleh dikosongkan, misalnya for (;;) yang berputar tanpa henti, tetapi bentuk kosong semacam ini hanya dipakai saat penghentiannya dikendalikan break di dalam badan. Untuk tabel, jadwal, dan deretan yang batasnya diketahui sejak awal, for adalah bentuk yang paling ringkas karena seluruh kendalinya terlihat dalam satu baris. Nama penghitungnya juga sebaiknya bermakna, misalnya baris dan kolom pada tabel, karena huruf tunggal seperti i hanya wajar pada putaran yang isinya sudah jelas.

Membaca langkah for satu per satu#

Demo berikut memutar perulangan for langkah demi langkah, mengizinkan nilai awal dan batasnya diubah, lalu mencatat setiap pemeriksaan kondisi beserta pembaruannya. Jalankan satu langkah berulang kali dan perhatikan urutannya.

Dengan nilai bawaan 1 sampai 5, kondisi ternyata diperiksa enam kali, yakni lima kali bernilai true untuk setiap putaran badan dan satu kali bernilai false yang mengakhiri perulangan. Selisih satu pemeriksaan ini sering terlewat, padahal inilah bukti bahwa badan selalu didahului pemeriksaan kondisi. Mengubah batas menjadi lebih kecil dari nilai awal memperlihatkan keadaan lain, yaitu kondisi langsung false dan badannya tidak pernah berjalan sama sekali. Urutan ini juga menjelaskan mengapa penghitung yang tidak pernah dinaikkan membuat perulangan tidak pernah selesai, karena pemeriksaan yang sama terus menghasilkan true. Membaca catatannya dari atas ke bawah memperlihatkan satu siklus utuh, yaitu pemeriksaan, badan, pembaruan, lalu pemeriksaan berikutnya.

while untuk jumlah putaran yang belum diketahui#

Perulangan while hanya memuat kondisi, dan badannya terus dijalankan selama kondisi itu true. Bentuk ini cocok ketika jumlah putaran tidak dapat dihitung di awal, misalnya mengurangi angka sampai habis, membaca data sampai penanda akhir, atau menunggu keadaan yang bisa tercapai pada putaran berapa pun.

java JumlahDigit.java
public class JumlahDigit {
    public static void main(String[] args) {
        int angka = 70534;
        int salinan = angka;
        int jumlah = 0;

        while (salinan > 0) {
            salinan = salinan / 10;
            jumlah = jumlah + 1;
        }

        System.out.println(angka + " memiliki " + jumlah + " digit.");
    }
}
text Keluaran
70534 memiliki 5 digit.

Setiap putaran membagi salinan dengan 10 dan menambah penghitung, sehingga putarannya bertahan persis sebanyak digit angkanya. Jumlah itu tidak diketahui di awal tanpa menghitung digit secara terpisah, dan while memecahkannya dengan menguji kondisi salinan > 0 pada setiap putaran. Setiap perulangan for sebenarnya dapat ditulis ulang sebagai while dengan inisialisasi di atasnya dan pembaruan di dalam badan, tetapi hasilnya lebih panjang dan kendalinya menyebar ke tiga tempat.

Bentuk while memang terlihat paling wajar ketika tidak ada penghitung yang perlu dijaga. Konsekuensinya satu, yaitu sesuatu di dalam badan harus menggerakkan kondisi mendekati false, pada contoh ini pembagiannya. Tanpa gerakan itu, perulangan berjalan tanpa akhir dan program terasa berhenti.

do-while yang dijamin berjalan sekali#

Perulangan do-while membalik urutannya, yaitu badan dijalankan lebih dahulu dan kondisi diperiksa sesudahnya, sehingga badan dijamin berjalan minimal satu kali. Bentuk ini pas untuk keadaan yang tindakannya wajib dicoba sekali sebelum bisa dinilai, seperti melempar dadu sampai muncul angka tertentu.

java LemparDadu.java
import java.util.Random;

public class LemparDadu {
    public static void main(String[] args) {
        Random acak = new Random(42);
        int lemparan;
        int hitung = 0;

        do {
            lemparan = acak.nextInt(6) + 1;
            hitung = hitung + 1;
            System.out.println("Lemparan " + hitung + ": " + lemparan);
        } while (lemparan != 6);

        System.out.println("Enam muncul pada lemparan ke-" + hitung);
    }
}
text Keluaran
Lemparan 1: 3
Lemparan 2: 4
Lemparan 3: 1
Lemparan 4: 3
Lemparan 5: 1
Lemparan 6: 2
Lemparan 7: 6
Enam muncul pada lemparan ke-7

Kelas Random yang dibuat dengan benih (seed) tetap menghasilkan deretan angka yang sama setiap kali program dijalankan, sehingga keluaran di atas dapat direproduksi. Putaran pertama terjadi sebelum kondisi lemparan != 6 sempat diperiksa, dan perulangan berhenti tepat setelah enam muncul pada lemparan ketujuh. Pola serupa muncul pada menu program dan pengulangan masukan, yaitu tampilkan pilihan, baca jawaban, lalu ulangi selama jawabannya belum sesuai syarat.

Bentuk ini lebih jarang dipakai dibandingkan for dan while, dan kehadirannya hampir selalu menandakan kebutuhan minimal satu kali itu. Perhatikan titik koma setelah while (lemparan != 6), tanda yang wajib ada karena do-while adalah satu pernyataan utuh. Menukar bentuk ini dengan while memaksa badannya diduplikasi sekali, karena lemparan pertama harus terjadi sebelum ada kondisi yang bisa diuji.

for-each untuk array dan koleksi#

Bentuk keempat, yang sering disebut for-each, menelusuri elemen satu per satu tanpa penghitung dengan bentuk for (Tipe elemen : kumpulan). Bentuk ini bekerja pada array maupun objek yang mengimplementasikan Iterable, misalnya List, dan menjadi pilihan pertama setiap kali indeks memang tidak dibutuhkan.

java RekapBelanja.java
import java.util.List;

public class RekapBelanja {
    public static void main(String[] args) {
        int[] harga = {12000, 8000, 15000, 5000};
        int total = 0;

        for (int nilai : harga) {
            total = total + nilai;
        }
        System.out.println("Total belanja: Rp" + total);

        List<String> buah = List.of("Apel", "Jeruk", "Pisang");
        for (String nama : buah) {
            System.out.println("Cek stok: " + nama);
        }
    }
}
text Keluaran
Total belanja: Rp40000
Cek stok: Apel
Cek stok: Jeruk
Cek stok: Pisang

Perulangan pertama menjumlahkan seluruh isi array tanpa satu pun harga[i], sedangkan yang kedua menelusuri List dengan pola yang sama. Kode yang sama dapat ditulis ulang dengan for klasik dan harga[i], tetapi bentuk ini menghilangkan indeks beserta risiko salah batasnya. Variabel elemennya berupa salinan nilai, sehingga menugaskan nilai baru kepadanya tidak mengubah isi array maupun list aslinya. Indeks tidak tersedia di dalam bentuk ini, sehingga tugas yang membutuhkan posisi, seperti membandingkan elemen dengan tetangganya, tetap memakai for klasik.

Perulangan bersarang#

Sebuah perulangan boleh berada di dalam badan perulangan lain, dan setiap satu langkah perulangan luar menuntut perulangan dalam menyelesaikan seluruh putarannya. Pola ini muncul pada tabel, grid, dan kombinasi dua dimensi.

java PolaBintang.java
public class PolaBintang {
    public static void main(String[] args) {
        for (int baris = 1; baris <= 4; baris++) {
            for (int kolom = 1; kolom <= baris; kolom++) {
                System.out.print("*");
            }
            System.out.println();
        }
    }
}
text Keluaran
*
**
***
****

Perulangan luar mengurus nomor baris, sedangkan perulangan dalam mencetak * sebanyak nomor baris itu tanpa berpindah baris, dan println() tanpa argumen menutup barisnya. Total putaran badan dalam berjumlah 1 + 2 + 3 + 4, yaitu sepuluh kali, karena batas perulangan dalam ikut bergantung pada nilai baris milik perulangan luarnya. Contoh lainnya adalah tabel perkalian lengkap, yaitu perulangan baris dari 1 sampai 10 yang badannya berisi perulangan kolom dengan batas yang sama. Selama perulangan dalam belum menuntaskan seluruh putarannya, perulangan luar menunggu di tempatnya, dan pola menunggu inilah yang membuat jumlah putarannya berlipat. Ketika kedua batas tidak saling bergantung, jumlah putarannya berlipat sesuai perkalian keduanya, sehingga bersarang dua tingkat pada ukuran n sudah berarti sekitar n dikali n pekerjaan.

Kesalahan umum#

  • Menulis kondisi i <= nilai.length pada perulangan array. Indeks terakhirnya adalah nilai.length - 1, sehingga satu langkah terakhir memicu ArrayIndexOutOfBoundsException. Pakai i < nilai.length.
  • Melupakan pembaruan di badan while, misalnya mengurangi salinan. Kondisinya tidak pernah berubah, badan berjalan tanpa akhir, dan program terasa membeku.
  • Mengira penugasan ke variabel elemen for-each mengubah kumpulannya. Variabel itu salinan elemen, sehingga perubahan harus dilakukan lewat indeks pada array atau lewat metode kumpulannya.
  • Menambah atau menghapus elemen List di tengah for-each. Struktur koleksinya berubah saat sedang ditelusuri, dan eksekusi berhenti dengan ConcurrentModificationException.
  • Menjadikan bilangan pecahan penghitung perulangan. Penambahan 0.1 berulang kali menumpuk pembulatan, sehingga kondisi berhenti bisa terlewati. Pakai bilangan bulat sebagai penghitung, lalu pecahkan nilainya saat dipakai.
  • Mengulang pekerjaan yang sama di dalam badan untuk nilai yang tidak berubah, misalnya membaca panjang array pada setiap putaran. Angkat perhitungan itu ke luar badan agar putarannya lebih ringan.
  • Menulis do-while tanpa titik koma setelah while (kondisi). Kompiler menolaknya karena pernyataan itu belum dianggap selesai.

Ringkasan#

  • for mengemas inisialisasi, kondisi, dan pembaruan dalam satu baris, cocok untuk putaran yang jumlahnya diketahui.
  • while memeriksa kondisi lebih dahulu untuk jumlah putaran yang belum diketahui, sedangkan do-while menjalankan badannya minimal satu kali.
  • for-each menelusuri array dan Iterable tanpa indeks, dan variabel elemennya berupa salinan nilai.
  • Perulangan bersarang melipatgandakan jumlah putaran, dan batas perulangan dalam boleh bergantung pada perulangan luar.

Referensi#