KezdőOlvasási idő: ~16 perc

Vezérlési szerkezetek

If-else, switch kifejezések, for, while, do-while ciklusok, break, continue és címkézett break

A vezérlési szerkezetek határozzák meg a program végrehajtási sorrendjét. Nélkülük minden kód egyetlen egyenes vonalú utasítássorozat lenne.


1. Definíció

Mi ez? A vezérlési szerkezetek (control structures) olyan nyelvi elemek, amelyek meghatározzák, hogy a program utasításai milyen sorrendben hajtódnak végre — feltételesen, ismételten, vagy éppen átugorva bizonyos blokkokat.

Miért fontos? Minden valós program tartalmaz döntéseket és ismétléseket. Az if, switch, for, while és társaik nélkül nem lehetne üzleti logikát, validációt, adatfeldolgozást vagy bármilyen dinamikus viselkedést implementálni.

Hol használjuk?

  • Üzleti szabályok kiértékelése (if/else, switch)
  • Kollekciók és tömbök bejárása (for, for-each, while)
  • Input validáció (do-while)
  • Állapotgépek és routing (switch expression)
  • Komplex iterációk vezérlése (break, continue, labeled break)

2. Alapfogalmak

2.1 Feltételes elágazások

Szerkezet Leírás Mikor használd
if / else Alapvető feltételes elágazás 1–3 ág, boolean feltétel
if / else if / else Többirányú feltétel Több kizáró feltétel
switch (classic) Értékalapú elágazás Egy változó több konkrét értéke
switch expression (Java 14+) Értéket visszaadó switch Változóhoz rendelés, tisztább szintaxis
Ternary ? : Inline feltétel Egyszerű érték-hozzárendelés

2.2 Ciklusok

Szerkezet Leírás Tipikus használat
for Számlálós ciklus Ismert iterációszám, index szükséges
for-each (for(T x : coll)) Iterátor-alapú ciklus Kollekció/tömb teljes bejárása
while Elöltesztelő ciklus Feltételtől függő ismétlés
do-while Hátultesztelő ciklus Legalább 1× végrehajtás szükséges

2.3 Vezérlésátadó utasítások

Kulcsszó Hatás
break Kilép az aktuális ciklusból vagy switch-ből
continue Átugorja az aktuális iteráció hátralévő részét
return Kilép a metódusból (és opcionálisan értéket ad vissza)
yield (Java 14+) Értéket ad vissza egy switch expression blokkból
labeled break Kilép egy megjelölt (labeled) külső ciklusból
labeled continue A megjelölt külső ciklus következő iterációjára ugrik

2.4 Switch evolúciója

Java 1.0classic switch (int, char)
Java 5    → enum támogatás
Java 7    → String támogatás
Java 14   → switch expression (preview → standard)
Java 17   → pattern matching switch (preview)
Java 21   → pattern matching switch (finalized)

2.5 Ciklusinvariáns, leállás és szándék

A jó ciklus nem csak szintaktikailag helyes, hanem szemantikailag is érthető.

  • A ciklusinvariáns valami olyan állítás, ami minden iteráció előtt és után igaz marad
  • A leállási feltétel megmagyarázza, hogy a ciklus miért fog véget érni
  • A szándék legyen egyértelmű: bejárás, keresés, újrapróbálás, aggregálás vagy várakozás

Ezek azért fontosak, mert sok hiba nem a szintaxisból, hanem a homályos vezérlési logikából származik.

Például a while(true) önmagában nem rossz. Akkor válik problémássá, ha a kilépési pont rejtett, az állapotváltozás nem átlátható, vagy az interruption kezelése hiányzik.


3. Gyakorlati használat

Melyik ciklust válaszd?

Szituáció Ajánlott szerkezet
Tömb/lista teljes bejárása, index nem kell for-each
Index szükséges vagy léptetés nem +1 for
Feltételtől függő ismétlés, 0× is lehet while
Legalább 1× végre kell hajtani (pl. menu) do-while
Végtelen ciklus szerviz-loophoz while(true) + break

Mikor `switch` vs `if-else`?

  • switch: egyetlen változó több konkrét értéke, enum routing, állapotgépek
  • if-else: boolean kifejezések, tartományellenőrzés (x > 10), több változó kombinációja
  • switch expression (Java 14+): ha az elágazás eredménye egy érték
  • Pattern matching switch (Java 21+): típusellenőrzés + casting egyben

Gyakorlati választási ellenőrzőlista

Használhatod ezt a gyors mentális modellt:

  1. Egyetlen érték alapján ágazol? Inkább switch.
  2. Tartományokat vagy több feltételt vizsgálsz? Inkább if-else.
  3. Kell az index? Klasszikus for.
  4. Csak bejárás kell? for-each.
  5. Legalább egyszer végre kell hajtani? do-while.
  6. Túl okossá válik a vezérlés? Refaktoráld kisebb metódusokra.

4. Kódpéldák

4.1 Classic switch — ❌ Fall-through hiba

// ❌ ROSSZ — hiányzó break, fall-through bug
switch (day) {
    case MONDAY:
        System.out.println("Hétfő");
        // break hiányzik!
    case TUESDAY:
        System.out.println("Kedd"); // MONDAY esetén is kiíródik!
        break;
    default:
        System.out.println("Egyéb nap");
}
// ✅ JÓ — explicit break minden ágban
switch (day) {
    case MONDAY:
        System.out.println("Hétfő");
        break;
    case TUESDAY:
        System.out.println("Kedd");
        break;
    default:
        System.out.println("Egyéb nap");
}

4.2 Switch expression (Java 14+)

// ✅ Modern switch expression — nincs fall-through, értéket ad vissza
String type = switch (statusCode) {
    case 200, 201 -> "Success";
    case 301, 302 -> "Redirect";
    case 404      -> "Not Found";
    case 500      -> "Server Error";
    default       -> "Unknown";
};

4.3 Switch expression `yield`-del

int numLetters = switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> 6;
    case TUESDAY                -> 7;
    default -> {
        String s = day.toString();
        int result = s.length();
        yield result; // yield adja vissza a block értékét
    }
};

4.4 Pattern matching switch (Java 21+)

// ✅ Típusellenőrzés + casting switch-ben
static String format(Object obj) {
    return switch (obj) {
        case Integer i -> String.format("int: %d", i);
        case Long l    -> String.format("long: %d", l);
        case String s  -> String.format("String: %s", s);
        case null      -> "null";
        default        -> obj.toString();
    };
}

4.5 Guarded pattern (Java 21+)

static String classify(Shape shape) {
    return switch (shape) {
        case Circle c when c.radius() > 100   -> "nagy kör";
        case Circle c                          -> "kis kör";
        case Rectangle r when r.area() > 1000  -> "nagy téglalap";
        case Rectangle r                       -> "kis téglalap";
        default                                -> "ismeretlen";
    };
}

4.6 Labeled break — többszintű ciklus elhagyása

// ✅ Labeled break a külső ciklusból való kilépéshez
outer:
for (int i = 0; i < matrix.length; i++) {
    for (int j = 0; j < matrix[i].length; j++) {
        if (matrix[i][j] == target) {
            System.out.println("Megtalálva: [" + i + "][" + j + "]");
            break outer; // a KÜLSŐ ciklusból lép ki
        }
    }
}

4.7 Off-by-one hiba

// ❌ ROSSZ — ArrayIndexOutOfBoundsException
for (int i = 0; i <= arr.length; i++) { // <= helyett < kell
    System.out.println(arr[i]);
}

// ✅ JÓ
for (int i = 0; i < arr.length; i++) {
    System.out.println(arr[i]);
}

4.8 Végtelen ciklus — break-kel kontrollálva

// ✅ Input validáció do-while ciklussal
int value;
do {
    System.out.print("Adj meg egy pozitív számot: ");
    value = scanner.nextInt();
} while (value <= 0);

4.9 Keresési minta korai `return`-nel

static int indexOf(int[] values, int target) {
    for (int index = 0; index < values.length; index++) {
        if (values[index] == target) {
            return index;
        }
    }
    return -1;
}

Ez sokszor tisztább, mint egy külső foundIndex változó fenntartása a ciklus körül.

4.10 Címkézett `continue` többszintű iterációhoz

outer:
for (String row : rows) {
    for (String cell : row.split(",")) {
        if (cell.isBlank()) {
            continue outer;
        }
    }
    System.out.println("Érvényes sor: " + row);
}

Ez érvényes Java, de csak visszafogottan érdemes használni. Ha üzleti logikát kezd elrejteni, akkor jobb kisebb helper metódusokra bontani.


5. Trade-off-ok

Szempont Részletek
Olvashatóság for-each > for > while a kollekció bejárásnál. switch expression > if-else lánc ha 4+ ág van.
Teljesítmény A JIT compiler a legtöbb ciklust azonos natív kódra optimalizálja. for-each tömbre ugyanaz mint az indexelt for.
switch vs if-else A switch egész és String esetén tableswitch/lookupswitch bytecode-ot generál — sok ág esetén gyorsabb mint az if-else lánc.
Fall-through Classic switch fall-through hasznos lehet szándékos csoportosításra, de hibaforrás. Switch expression-ben nincs fall-through.
Labeled break Erős eszköz, de túlzott használata nehezen olvashatóvá teszi a kódot — refaktoráld metódusba.

6. Gyakori hibák

Hiba Leírás Megelőzés
Hiányzó break Classic switch esetén akaratlan fall-through Ha lehet, használj switch expressiont
Off-by-one <= használata < helyett tömbindexelésnél Ha nem kell index, inkább for-each
Végtelen ciklus A feltétel soha nem lesz false Ellenőrizd, hogy az állapot minden iterációban változik
Lusta kiértékelés félreértése Mellékhatásos logika elrejtése && / || feltételben Tartsd deklaratívnak a feltételt, a módosítást vidd külön sorba
Módosítatlan ciklusváltozó while ciklusban elmarad a számláló frissítése Ha számláló vezérli a ciklust, gyakran jobb a for
Null kezelés hiánya switch-ben Régebbi Java-verziókban könnyen NullPointerException lesz Kezeld előre a null-t, vagy modern Java-ban használj case null -> ágat

7. Mélymerülés

7.1 Switch expression mint exhaustive match

Java 14+-tól a switch expression kötelezően exhaustive — minden lehetséges értéket le kell fedni, különben fordítási hiba. Enum-oknál ez azt jelenti, hogy vagy minden enum konstanst kezeled, vagy van default ág.

7.2 Sealed classes + switch (Java 17/21)

sealed interface Shape permits Circle, Rectangle, Triangle {}
record Circle(double r) implements Shape {}
record Rectangle(double w, double h) implements Shape {}
record Triangle(double a, double b, double c) implements Shape {}

// Exhaustive — nem kell default ha minden permitted class-t kezeled
double area(Shape shape) {
    return switch (shape) {
        case Circle c    -> Math.PI * c.r() * c.r();
        case Rectangle r -> r.w() * r.h();
        case Triangle t  -> {
            double s = (t.a() + t.b() + t.c()) / 2;
            yield Math.sqrt(s * (s - t.a()) * (s - t.b()) * (s - t.c()));
        }
    };
}

7.3 JIT optimalizációk ciklusokra

  • Loop unrolling: a JIT kicsomagolja a kis fix méretű ciklusokat
  • Loop vectorization: SIMD utasításokat használ ahol lehetséges
  • Bounds check elimination: ha bizonyítható, hogy az index mindig érvényes, a JIT eltávolítja a határellenőrzéseket
  • Loop inversion: while-t do-while-lá alakítja a jobb branch prediction érdekében

7.4 Bytecode szinten

  • tableswitch: összefüggő értékkészletre (O(1) lookup)
  • lookupswitch: szórt értékkészletre (O(log n) binary search)
  • String switch: hashCode() alapú lookupswitch + equals() ellenőrzés

7.5 A strukturált vezérlés jobban öregszik

A junior kód gyakran a “kevesebb sorra”, a senior kód inkább a “kevesebb meglepetésre” optimalizál. A vezérlési szerkezeteknél ez különösen igaz.

  • egy rövid if sokszor tisztább, mint egy egymásba ágyazott ternary
  • egy külön metódus sokszor tisztább, mint a címkézett vezérlésátadás
  • egy korai return sokszor tisztább, mint a mélyen egymásba ágyazott blokkok

A jó vezérlés csökkenti azt az állapotmennyiséget, amit az olvasónak fejben kell tartania.

7.6 Ciklusok vs magasabb szintű absztrakciók

Nem minden ciklusnak kell örökre ciklusnak maradnia. Ahogy bővül a tudás, sok bejárási feladat jobban kifejezhető streamekkel, collectorkkel vagy könyvtári absztrakciókkal.

A klasszikus ciklusok viszont továbbra is fontosak, ha:

  • explicit indexkezelés kell
  • kritikus hot path-ban fut a kód
  • komplex korai kilépési logika szükséges
  • az imperatív változat egyszerűbben érthető

8. Interjúkérdések

Mikor jobb a `switch`, mint az `if-else`?

Amikor egyetlen kifejezés dönti el az ágat, és a lehetséges értékek konkrétak és végesek, például enumok, státuszkódok vagy parancsnevek. Tartományokhoz és összetett boolean feltételekhez az if-else jobb.

Mi a különbség a `for-each` és a klasszikus `for` között?

A for-each teljes bejárásra jó, amikor nem kell index. A klasszikus for akkor jobb, ha kell az index, egyedi léptetés, visszafelé bejárás vagy több adatszerkezet összehangolt kezelése.

Miért tekinthető biztonságosabbnak a switch expression, mint a klasszikus switch?

Mert kizárja a véletlen fall-through-t, támogatja a teljes lefedést, és tisztán egy értéket ad vissza. Emiatt kevesebb hibára ad lehetőséget.

A `while(true)` mindig code smell?

Nem. Elfogadható eseményciklusoknál vagy retry logikánál, ha a kilépési feltételek világosak. Akkor lesz smell, ha a leállás rejtett állapoton múlik, vagy az interruption / shutdown kezelés hiányzik.


9. Szójegyzék

Magyar kifejezés Angol kifejezés Jelentés
Vezérlési szerkezet Control structure A végrehajtási sorrend irányítása
Feltételes elágazás Conditional branch if/else, ternary
Ciklus Loop Ismétlődő végrehajtás
Elöltesztelő ciklus Pre-test loop while — feltétel előbb
Hátultesztelő ciklus Post-test loop do-while — feltétel utóbb
Címkézett break Labeled break Külső ciklusból kilépés
Fall-through Fall-through Switch ág "átesés" a következőbe
Switch expression Switch expression Értéket visszaadó switch (Java 14+)
Pattern matching Pattern matching Típusellenőrzés + destrukturálás
Exhaustive Exhaustive Teljes lefedés (minden eset kezelve)
Guarded pattern Guarded pattern when feltételes pattern
Yield Yield Blokk-értéket visszaadó kulcsszó

10. Gyorsreferencia

  • if/else — boolean feltételek, 1–3 ág
  • switch (classic) — egy változó konkrét értékei, vigyázz a break-re
  • switch expression (Java 14+)-> szintaxis, nincs fall-through, értéket ad vissza
  • Pattern matching switch (Java 21+) — típusellenőrzés, when guard, case null
  • for — ismert iterációszám, index szükséges
  • for-each — kollekció/tömb bejárása, nincs index
  • while — 0 vagy több ismétlés, feltétel elöl
  • do-while — legalább 1× végrehajtás, feltétel hátul
  • break — kilépés ciklusból/switch-ből
  • continue — ugrás a következő iterációra
  • return — kilépés metódusból
  • yield — érték visszaadása switch expression blokkból
  • Labeled break/continue — többszintű ciklusok vezérlése
  • Sealed classes + switch — exhaustive pattern matching, default nélkül
  • JIT: tableswitch = O(1), lookupswitch = O(log n)

🎮 Játékok

10 kérdés