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
}Başlangıç: Sayaç değişkeni tanımlanır (bir kez çalışır).
Koşul: Her turda kontrol edilir.
trueise döngü devam eder,falseise biter.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ış:
int i = 1→ sayaç başlıyori <= 10→ true mi? Evet → gövdeyi çalıştıri++→ i şimdi 2i <= 10→ true mi? Evet → gövdeyi çalıştır... ta ki
i = 11olana kadar →11 <= 10false → 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 20Gü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ü bitincei'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); // 5Gü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.9for-each'in Sınırlamaları
for-each her durumda kullanılamaz:
İndeks lazımsa: for-each indeks vermez.
Elemanı değiştirmek istiyorsan: for-each kopyayla çalışır, orijinali değiştiremezsin.
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?
| Durum | En İyi Döngü |
|---|---|
| Kaç kez tekrar edeceğini biliyorsun | for |
| Dizi/koleksiyon üzerinde gezinme | for-each |
| Koşula bağlı, kaç kez belirsiz | while |
| En az bir kez çalışmalı | do-while |
| Sonsuz döngü (sunucu, oyun) | while (true) |
| İndeks lazım | for (klasik) |
| Sadece okuma, indeks gereksiz | for-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'lerdeStringBuilderkullan:
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 377Faktoriyel 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 = 1024while 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 = 0vsi = 1farkını iyi anla.Sonsuz döngüden kaçın — güncelleme ifadesini unutma, koşulun bir noktada
falseolacağından emin ol.
AI Asistan
Sorularını yanıtlamaya hazır