← Kursa Dön
📄 Text · 15 min

for, while, do-while Döngüleri

Giriş — Neden Döngü?

Diyelim ki ekrana 1'den 100'e kadar sayıları yazdırmak istiyorsun. 100 tane System.out.println() mı yazacaksın? Tabii ki hayır. İşte döngüler, tekrarlayan işleri otomatikleştirmek için var.

Programlamadaki en güçlü kavramlardan biri döngüdür. Bir işlemi belirli bir koşul sağlandığı sürece tekrar tekrar çalıştırır. Java'da üç temel döngü yapısı var: for, while ve do-while.

Analoji: Döngüyü bir çamaşır makinesi gibi düşün. "Suyu doldur, yıka, durula, sık" döngüsünü programladığın sayıda tekrarlar. Koşul sağlanmadığında (program bittiğinde) durur.


for Döngüsü

for döngüsü, kaç kez tekrar edeceğini bildiğin durumlarda kullanılır. Üç bileşeni var:

for (başlangıç; koşul; güncelleme) {
    // tekrarlanan kod
}
  1. Başlangıç: Sayaç değişkeni tanımlanır (bir kez çalışır).

  2. Koşul: Her turda kontrol edilir. true ise döngü devam eder, false ise biter.

  3. Güncelleme: Her turun sonunda çalışır (genellikle sayaç artırılır).

Örnek: 1'den 10'a Kadar Sayılar

for (int i = 1; i <= 10; i++) {
    System.out.println(i);
}

Akış:

  1. int i = 1 → sayaç başlıyor

  2. i <= 10 → true mi? Evet → gövdeyi çalıştır

  3. i++ → i şimdi 2

  4. i <= 10 → true mi? Evet → gövdeyi çalıştır

  5. ... ta ki i = 11 olana kadar → 11 <= 10 false → döngü biter

Örnek: Geriye Sayma

for (int i = 10; i >= 1; i--) {
    System.out.print(i + " ");
}
System.out.println("Kalkış!");
// 10 9 8 7 6 5 4 3 2 1 Kalkış!

Sayaç artmak zorunda değil — azaltabilirsin de.

Örnek: İkişer İkişer Atlama

for (int i = 0; i <= 20; i += 2) {
    System.out.print(i + " ");
}
// 0 2 4 6 8 10 12 14 16 18 20

Güncelleme kısmında i++ yerine i += 2, i *= 2, hatta i = i * 3 + 1 gibi herhangi bir ifade yazabilirsin.

Örnek: Toplam Hesaplama

int toplam = 0;

for (int i = 1; i <= 100; i++) {
    toplam += i;
}

System.out.println("1'den 100'e toplam: " + toplam); // 5050

💡 İpucu: for döngüsünde tanımlanan sayaç değişkeni (int i) sadece döngü içinde geçerlidir. Döngü bitince i'ye erişemezsin. Bu iyi bir şey — kapsamı sınırlı tutmak hataları azaltır.

for (int i = 0; i < 5; i++) {
    System.out.println(i);
}
// System.out.println(i); // HATA! i burada tanımlı değil.

for Döngüsünün Esnek Kullanımları

for döngüsünün üç bileşeni de isteğe bağlıdır. Herhangi birini boş bırakabilirsin:

Başlangıç Dışarıda

int i = 0;
for (; i < 5; i++) {
    System.out.println(i);
}
// i hâlâ erişilebilir: System.out.println("Son i: " + i); // 5

Güncelleme İçeride

for (int i = 0; i < 5; ) {
    System.out.println(i);
    i++;  // güncelleme gövdede
}

Birden Fazla Sayaç

for (int i = 0, j = 10; i < j; i++, j--) {
    System.out.println("i=" + i + ", j=" + j);
}
// i=0, j=10
// i=1, j=9
// i=2, j=8
// i=3, j=7
// i=4, j=6

İki sayaç birbirine yaklaşıyor — i < j olduğu sürece devam.


while Döngüsü

while döngüsü, koşul true olduğu sürece tekrar eder. Kaç kez tekrar edeceğini önceden bilmediğin durumlarda idealdir.

while (koşul) {
    // koşul true olduğu sürece çalışır
}

Koşul her turda başta kontrol edilir. Eğer başlangıçta false ise gövde hiç çalışmaz.

Örnek: 1'den 10'a (while ile)

int i = 1;

while (i <= 10) {
    System.out.println(i);
    i++;
}

for ile aynı işi yapıyor ama parçalar ayrılmış: başlangıç yukarıda, koşul parantezde, güncelleme gövdede.

Örnek: Kullanıcıdan Giriş Alma

Scanner scanner = new Scanner(System.in);
String girdi = "";

while (!girdi.equals("çık")) {
    System.out.print("Komut girin (çıkmak için 'çık'): ");
    girdi = scanner.nextLine();
    System.out.println("Girdiniz: " + girdi);
}

System.out.println("Program sonlandı.");

Bu örnekte kullanıcı "çık" yazana kadar döngü devam eder. Kaç kez tekrar edeceği belli değil — while için mükemmel bir senaryo.

Örnek: Basamak Sayısı Bulma

int sayi = 123456;
int basamakSayisi = 0;
int temp = sayi;

while (temp > 0) {
    temp /= 10;  // son basamağı at
    basamakSayisi++;
}

System.out.println(sayi + " sayısı " + basamakSayisi + " basamaklı");
// 123456 sayısı 6 basamaklı

Her turda sayıyı 10'a bölüyoruz (son basamağı atıyoruz) ve sayacı artırıyoruz. Sayı 0 olunca döngü biter.


do-while Döngüsü

do-while döngüsü while'a benzer ama bir farkla: koşul sonda kontrol edilir. Bu, gövdenin en az bir kez çalışmasını garanti eder.

do {
    // en az bir kez çalışır
} while (koşul);

Sonundaki noktalı virgülü (;) unutma!

Örnek: Menü Sistemi

Scanner scanner = new Scanner(System.in);
int secim;

do {
    System.out.println("\n=== MENÜ ===");
    System.out.println("1. Oyun Başlat");
    System.out.println("2. Ayarlar");
    System.out.println("3. Çıkış");
    System.out.print("Seçiminiz: ");
    secim = scanner.nextInt();

    switch (secim) {
        case 1 -> System.out.println("Oyun başlıyor...");
        case 2 -> System.out.println("Ayarlar açılıyor...");
        case 3 -> System.out.println("Hoşça kalın!");
        default -> System.out.println("Geçersiz seçim!");
    }
} while (secim != 3);

Menü en az bir kez gösterilmeli — do-while bunun için biçilmiş kaftan. Kullanıcı 3'ü seçene kadar menü tekrar gösterilir.

Örnek: Geçerli Giriş Doğrulama

Scanner scanner = new Scanner(System.in);
int yas;

do {
    System.out.print("Yaşınızı girin (1-120): ");
    yas = scanner.nextInt();
    
    if (yas < 1 || yas > 120) {
        System.out.println("Geçersiz yaş! Tekrar deneyin.");
    }
} while (yas < 1 || yas > 120);

System.out.println("Yaşınız: " + yas);

Kullanıcıdan en az bir kez giriş almamız gerekiyor, sonra geçerli olup olmadığını kontrol ediyoruz.

while vs do-while

int x = 100;

// while — koşul başta kontrol, gövde hiç çalışmaz
while (x < 10) {
    System.out.println("while: " + x);  // ÇALIŞMAZ
    x++;
}

// do-while — gövde bir kez çalışır, sonra koşul kontrol
do {
    System.out.println("do-while: " + x);  // ÇALIŞIR (bir kez)
    x++;
} while (x < 10);

x = 100 olduğu için x < 10 baştan false. while gövdeye hiç girmez ama do-while bir kez girer.

⚠️ Dikkat: do-while pratikte az kullanılır. Çoğu durumda while veya for yeterlidir. do-while'ı sadece "en az bir kez çalışmalı" mantığı gerektiren durumlarda kullan (menü, giriş doğrulama gibi).


for-each (Enhanced for) Döngüsü

Bir dizi veya koleksiyon üzerinde gezinmek istediğinde for-each en temiz yoldur:

for (Tip eleman : koleksiyon) {
    // her eleman için çalışır
}

Örnek: Dizi Üzerinde Gezinme

int[] sayilar = {10, 20, 30, 40, 50};

for (int sayi : sayilar) {
    System.out.println(sayi);
}

Bu, aşağıdaki klasik for ile eşdeğer ama çok daha temiz:

for (int i = 0; i < sayilar.length; i++) {
    System.out.println(sayilar[i]);
}

Örnek: String Dizisi

String[] meyveler = {"Elma", "Armut", "Portakal", "Muz"};

for (String meyve : meyveler) {
    System.out.println("Meyve: " + meyve);
}

Örnek: Toplam ve Ortalama

double[] notlar = {85.5, 92.0, 78.5, 90.0, 88.5};
double toplam = 0;

for (double not : notlar) {
    toplam += not;
}

double ortalama = toplam / notlar.length;
System.out.printf("Ortalama: %.1f%n", ortalama); // 86.9

for-each'in Sınırlamaları

for-each her durumda kullanılamaz:

  1. İndeks lazımsa: for-each indeks vermez.

  2. Elemanı değiştirmek istiyorsan: for-each kopyayla çalışır, orijinali değiştiremezsin.

  3. Geriye doğru gezinmek istiyorsan: for-each sadece ileri gider.

int[] sayilar = {1, 2, 3, 4, 5};

// Bu çalışmaz — elemanı değiştiremezsin
for (int sayi : sayilar) {
    sayi = sayi * 2;  // Sadece kopyayı değiştirir!
}
// sayilar hâlâ {1, 2, 3, 4, 5}

// Eleman değiştirmek için klasik for kullan
for (int i = 0; i < sayilar.length; i++) {
    sayilar[i] = sayilar[i] * 2;  // Orijinali değiştirir
}
// sayilar şimdi {2, 4, 6, 8, 10}

💡 İpucu: İndekse ihtiyacın yoksa ve sadece okuma yapıyorsan her zaman for-each tercih et. Daha az hata yapma ihtimali, daha okunabilir kod.


Sonsuz Döngü

Koşul her zaman true ise döngü sonsuza kadar çalışır. Bu genellikle bir hatadır ama bazen kasıtlı kullanılır.

for ile Sonsuz Döngü

for (;;) {
    System.out.println("Sonsuza kadar...");
}

Üç bileşen de boş bırakılmış — koşul varsayılan olarak true.

while ile Sonsuz Döngü

while (true) {
    System.out.println("Sonsuza kadar...");
}

Bu daha yaygın ve okunabilir.

Kasıtlı Sonsuz Döngü — Sunucu Bekleme

while (true) {
    System.out.println("İstek bekleniyor...");
    // İstek geldiğinde işle
    // Belirli bir koşulda break ile çık
    
    String komut = scanner.nextLine();
    if (komut.equals("shutdown")) {
        System.out.println("Sunucu kapatılıyor.");
        break;  // Döngüden çık
    }
}

Sunucular, oyun döngüleri, event loop'lar — bunlar hep kasıtlı sonsuz döngü kullanır. Çıkış koşulu break ile sağlanır.

⚠️ Dikkat: Yanlışlıkla sonsuz döngü yazmak programı kilitler. Döngü koşulunun bir noktada false olacağından emin ol. Güncelleme ifadesini unutmak en yaygın hata:

int i = 0;
while (i < 10) {
    System.out.println(i);
    // i++ UNUTTUN! → sonsuz döngü, hep 0 yazdırır
}

İç İçe Döngüler (Nested Loops)

Bir döngünün içine başka bir döngü yazabilirsin. Dış döngünün her turunda iç döngü baştan sona çalışır.

Örnek: Çarpım Tablosu

for (int i = 1; i <= 10; i++) {
    for (int j = 1; j <= 10; j++) {
        System.out.printf("%4d", i * j);
    }
    System.out.println();  // Satır sonu
}

Çıktı (kısaltılmış):

   1   2   3   4   5   6   7   8   9  10
   2   4   6   8  10  12  14  16  18  20
   3   6   9  12  15  18  21  24  27  30
...

Dış döngü satırları, iç döngü sütunları temsil eder.

Örnek: Yıldız Üçgeni

int satir = 5;

for (int i = 1; i <= satir; i++) {
    for (int j = 1; j <= i; j++) {
        System.out.print("* ");
    }
    System.out.println();
}

Çıktı:

* 
* * 
* * * 
* * * * 
* * * * * 

Örnek: 2D Dizi Gezinme

int[][] matris = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};

for (int i = 0; i < matris.length; i++) {
    for (int j = 0; j < matris[i].length; j++) {
        System.out.printf("%3d", matris[i][j]);
    }
    System.out.println();
}

2D dizilerde for-each da kullanabilirsin:

for (int[] satir : matris) {
    for (int eleman : satir) {
        System.out.printf("%3d", eleman);
    }
    System.out.println();
}

Döngülerde Yaygın Hatalar

1. Off-by-One (Bir Eksik/Fazla)

// 1'den 10'a — 10 dahil mi?
for (int i = 1; i <= 10; i++) { }  // 10 dahil — 10 kez
for (int i = 1; i < 10; i++) { }   // 10 hariç — 9 kez

// 0'dan başlayıp 5 eleman
for (int i = 0; i < 5; i++) { }    // 0,1,2,3,4 — doğru
for (int i = 0; i <= 5; i++) { }   // 0,1,2,3,4,5 — 6 eleman!

Kural: Dizi gezinirken `i < dizi.length` kullan, <= değil.

2. Güncelleme Unutma

int i = 0;
while (i < 5) {
    System.out.println(i);
    // i++ unuttun → sonsuz döngü!
}

3. Koşulu Yanlış Yönde Yazma

// HATALI — i başta 10, 10 < 1 false, hiç çalışmaz
for (int i = 10; i < 1; i++) {
    System.out.println(i);
}

// DOĞRU — geriye sayarken koşul ve güncellemeyi uyumlu yap
for (int i = 10; i >= 1; i--) {
    System.out.println(i);
}

4. for-each'te ConcurrentModificationException

List<String> liste = new ArrayList<>(Arrays.asList("a", "b", "c"));

// HATALI — gezinirken silmek patlama yapar!
for (String s : liste) {
    if (s.equals("b")) {
        liste.remove(s);  // ConcurrentModificationException!
    }
}

// DOĞRU — Iterator kullan veya removeIf
liste.removeIf(s -> s.equals("b"));

Döngü Seçim Rehberi

Hangi döngüyü ne zaman kullanalım?

DurumEn İyi Döngü
Kaç kez tekrar edeceğini biliyorsunfor
Dizi/koleksiyon üzerinde gezinmefor-each
Koşula bağlı, kaç kez belirsizwhile
En az bir kez çalışmalıdo-while
Sonsuz döngü (sunucu, oyun)while (true)
İndeks lazımfor (klasik)
Sadece okuma, indeks gereksizfor-each

Karar Ağacı

Tekrar sayısını biliyor musun?
├── Evet → for
└── Hayır → En az 1 kez çalışmalı mı?
    ├── Evet → do-while
    └── Hayır → while
    
Dizi/koleksiyon mu geziyon?
├── Sadece okuma → for-each
└── Eleman değiştirme veya indeks lazım → for (klasik)

Pratik Örnek: Asal Sayı Kontrolü

int sayi = 29;
boolean asalMi = true;

if (sayi < 2) {
    asalMi = false;
} else {
    for (int i = 2; i <= Math.sqrt(sayi); i++) {
        if (sayi % i == 0) {
            asalMi = false;
            break;
        }
    }
}

System.out.println(sayi + (asalMi ? " asal" : " asal değil"));

Math.sqrt(sayi)'ye kadar kontrol etmek yeterlidir — optimizasyon açısından önemli bir detay.

Pratik Örnek: FizzBuzz

Klasik programlama problemi — 1'den 100'e kadar sayılar:

  • 3'e bölünüyorsa "Fizz"

  • 5'e bölünüyorsa "Buzz"

  • Her ikisine de bölünüyorsa "FizzBuzz"

  • Hiçbirine bölünmüyorsa sayının kendisi

for (int i = 1; i <= 100; i++) {
    if (i % 15 == 0) {
        System.out.println("FizzBuzz");
    } else if (i % 3 == 0) {
        System.out.println("Fizz");
    } else if (i % 5 == 0) {
        System.out.println("Buzz");
    } else {
        System.out.println(i);
    }
}

Pratik Örnek: Sayı Tahmin Oyunu

import java.util.Random;
import java.util.Scanner;

Random random = new Random();
int hedef = random.nextInt(100) + 1; // 1-100 arası
Scanner scanner = new Scanner(System.in);
int tahmin;
int deneme = 0;

System.out.println("1-100 arası bir sayı tuttum. Tahmin et!");

do {
    System.out.print("Tahminin: ");
    tahmin = scanner.nextInt();
    deneme++;

    if (tahmin < hedef) {
        System.out.println("Daha büyük!");
    } else if (tahmin > hedef) {
        System.out.println("Daha küçük!");
    } else {
        System.out.println("Bildin! " + deneme + " denemede buldun.");
    }
} while (tahmin != hedef);

Döngü Performans İpuçları

1. Döngü İçinde Gereksiz Hesaplama Yapma

// KÖTÜ — her turda length() hesaplanır
for (int i = 0; i < expensiveList.size(); i++) { ... }

// İYİ — bir kez hesapla
int boyut = expensiveList.size();
for (int i = 0; i < boyut; i++) { ... }

Not: Modern JVM'ler basit durumlarda bunu optimize eder, ama karmaşık ifadelerde dikkatli ol.

2. Büyük Koleksiyonlarda for-each Tercih Et

for-each, Iterator kullanır ve çoğu koleksiyonda indeksli erişimden daha verimlidir (özellikle LinkedList'te).

3. İç İçe Döngülerde Büyük Döngüyü Dışa Koy

// Daha verimli — dış döngü küçük
for (int i = 0; i < 10; i++) {        // 10 kez
    for (int j = 0; j < 10000; j++) {  // 10000 kez
        // ...
    }
}

Döngülerle String İşlemleri

String'ler karakter dizileridir ve döngülerle çok sık işlenir.

Örnek: Karakter Karakter Gezinme

String kelime = "Merhaba";

for (int i = 0; i < kelime.length(); i++) {
    char c = kelime.charAt(i);
    System.out.println("Index " + i + ": " + c);
}

Örnek: String Ters Çevirme

String orijinal = "Java";
String ters = "";

for (int i = orijinal.length() - 1; i >= 0; i--) {
    ters += orijinal.charAt(i);
}

System.out.println(ters); // avaJ

💡 İpucu: Döngü içinde string birleştirme (+=) performans açısından kötüdür çünkü her seferinde yeni String nesnesi oluşur. Uzun string'lerde StringBuilder kullan:

String orijinal = "Java";
StringBuilder sb = new StringBuilder();

for (int i = orijinal.length() - 1; i >= 0; i--) {
    sb.append(orijinal.charAt(i));
}

System.out.println(sb.toString()); // avaJ

Örnek: Sesli Harf Sayma

String metin = "Programlama öğrenmek eğlencelidir";
int sesliSayisi = 0;
String sesliler = "aeıioöuüAEIİOÖUÜ";

for (int i = 0; i < metin.length(); i++) {
    if (sesliler.indexOf(metin.charAt(i)) != -1) {
        sesliSayisi++;
    }
}

System.out.println("Sesli harf sayısı: " + sesliSayisi);

Döngülerle Matematiksel Problemler

Fibonacci Serisi

Her sayı, kendinden önceki iki sayının toplamıdır: 0, 1, 1, 2, 3, 5, 8, 13, ...

int n = 15;
long a = 0, b = 1;

System.out.print(a + " " + b + " ");

for (int i = 2; i < n; i++) {
    long c = a + b;
    System.out.print(c + " ");
    a = b;
    b = c;
}
// 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377

Faktoriyel Hesaplama

int n = 10;
long faktoriyel = 1;

for (int i = 1; i <= n; i++) {
    faktoriyel *= i;
}

System.out.println(n + "! = " + faktoriyel); // 10! = 3628800

Üs Hesaplama (Döngüyle)

int taban = 2;
int us = 10;
long sonuc = 1;

for (int i = 0; i < us; i++) {
    sonuc *= taban;
}

System.out.println(taban + "^" + us + " = " + sonuc); // 2^10 = 1024

while ile Sentinel (Nöbetçi) Deseni

Sentinel deseni, özel bir değer gelene kadar döngüye devam etmektir. Veri girişi ve dosya okumada çok yaygındır.

Scanner scanner = new Scanner(System.in);
int toplam = 0;
int sayac = 0;

System.out.println("Notları girin (-1 ile bitirin):");

int not = scanner.nextInt();

while (not != -1) {
    toplam += not;
    sayac++;
    not = scanner.nextInt();
}

if (sayac > 0) {
    double ortalama = (double) toplam / sayac;
    System.out.printf("%d not girildi, ortalama: %.1f%n", sayac, ortalama);
} else {
    System.out.println("Hiç not girilmedi.");
}

Burada -1 sentinel (nöbetçi) değerdir. Gerçek bir not değil, "bitti" anlamına gelir.


Döngü ile Dizi Arama

Lineer Arama

int[] sayilar = {45, 23, 78, 12, 56, 89, 34};
int aranan = 56;
int bulunanIndex = -1;

for (int i = 0; i < sayilar.length; i++) {
    if (sayilar[i] == aranan) {
        bulunanIndex = i;
        break;  // Bulduk, aramaya devam etmeye gerek yok
    }
}

if (bulunanIndex != -1) {
    System.out.println(aranan + " index " + bulunanIndex + "'de bulundu");
} else {
    System.out.println(aranan + " bulunamadı");
}

En Büyük ve En Küçük Elemanı Bulma

int[] dizi = {34, 12, 78, 56, 23, 89, 45};
int enBuyuk = dizi[0];
int enKucuk = dizi[0];

for (int i = 1; i < dizi.length; i++) {
    if (dizi[i] > enBuyuk) {
        enBuyuk = dizi[i];
    }
    if (dizi[i] < enKucuk) {
        enKucuk = dizi[i];
    }
}

System.out.println("En büyük: " + enBuyuk);  // 89
System.out.println("En küçük: " + enKucuk);  // 12

Özet

  • `for` — tekrar sayısı belli ise kullan. Başlangıç, koşul ve güncelleme tek satırda.

  • `while` — koşula bağlı tekrar, kaç kez belirsiz. Koşul başta kontrol edilir, gövde hiç çalışmayabilir.

  • `do-while` — while gibi ama gövde en az bir kez çalışır. Menü ve giriş doğrulama için ideal.

  • `for-each` — dizi/koleksiyon gezinmede en temiz yol. Sadece okuma için, indeks gerekmiyorsa tercih et.

  • Off-by-one hatalarına dikkat< vs <=, i = 0 vs i = 1 farkını iyi anla.

  • Sonsuz döngüden kaçın — güncelleme ifadesini unutma, koşulun bir noktada false olacağından emin ol.