Elementarz Java #5 – Pętle
Wstęp
Dzisiaj w ramach cyklu #ElementarzJava artykuł na temat pętli. Proste zagadnienie ale jak zwykle mam nadzieję, że i tym razem czymś Ciebie zaskoczyłem. Zapraszam do dalszej lektury i jak zwykle pozostaje mi życzyć owocnej nauki.
Seria #ElementarzJava składa się z następujących artykułów:
- Podstawy języka Java (kompilacja, zmienne, struktura klasy, pakiety),
- Typy danych w języku Java (deklaracja i inicjalizacja zmiennych, różnica między typami, garbage collection, typy opakowujące),
- Operatory i konstrukcje warunkowe w Java (użycie operatorów, porównywanie obiektów, instrukcje:
if
,if/else
,switch
), - Tablice (charakterystyka tablic, tablice jedno i wielowymiarowe),
- Pętle (tworzenie pętli, instrukcje
break
icontinue
, etykiety), - Metody oraz hermetyzacja (metody statyczne, przeciążanie metod, konstruktory, modyfikatory widoczności, hermetyzacja elementów klasy, parametry),
- Dziedziczenie (implementacja, przysłanianie metod, polimorfizm, rzutowanie),
- Obsługa wyjątków (kategorie wyjątków, łapanie wyjątków, klasy wyjątków),
- API Java (
String
,StringBuilder
, data i czas, kolekcje, wyrażenia lambda).
Jesteś tu pierwszy raz? Polecam rozpocząć lekturę od materiału: Elementarz Java #0 – wprowadzenie.
Tworzenie oraz użycie pętli while
Pętla while
używana jest do wykonywania zestawu instrukcji do momentu, kiedy podany warunek logiczny nie osiągnie wartości false
. Pętla ta sprawdza podany warunek przed rozpoczęciem działania.
Jak wygląda pętla while
?
boolean condition = true; while(condition) { System.out.println("Hello"); conditione = false; }
Jeśli w pętli mamy tylko jedną instrukcję nawiasy klamrowe mogą zostać pominięte.
boolean condition = true; while(condition) System.out.println("Hello");
Powyższa pętla będzie działać w „nieskończoność”.
Tworzenie oraz użycie pętli for
W Javie wyróżniamy dwie wersje pętli iteracyjnej for
, tradycyjną oraz pętlę for-each
. Tradycyjna pętla for
jest zazwyczaj używana do wykonywania zestawu poleceń określoną liczbę razy. Składa się ona z trzech instrukcji oddzielonych średnikami: inicjalizacji, warunku zakończenia oraz klauzuli aktualizującej. Definicja każdej z tych trzech instrukcji jest opcjonalna. Poniższy kod będzie więc całkowicie poprawny.
for(;;) { }
Również zdefiniowanie choćby tylko jednej z tych instrukcji nie będzie powodowało błędu.
int i = 0; for(;i<1;) { System.out.print(i); i++; }
Ten kod zadziała poprawnie i wypisze na ekran cyfrę 0
. Jeśli dodatkowo warunek w bloku for
został by zmieniony na i<0
to wtedy pętla nie została by wykonana ani razu. Warunek ten sprawdzany jest przed każdym wywołaniem for
.
Co ciekawe w bloku inicjalizacyjnym pętli for
możemy inicjalizować więcej niż jedną wartość. Zasada jest tutaj jedna, zmienne muszą być tego samego typu.
for(int i = 0, y = 1;i<2;i++) { System.out.println(y); }
Po uruchomieniu powyższego kodu na ekranie komputera dwukrotnie zostanie wypisana wartość zmiennej y
czyli liczba 1
. Dodatkowo, każda zmienna zdefiniowana w bloku inicjalizującym pętli for
, może być również aktualizowana w klauzuli aktualizującej.
for(int i = 0, y = 2;i< 2;i++, y--) { System.out.println(y); }
Taki kod wypisze na ekranie liczbę 2
i 1
.
Warunek logiczny jaki umieszczamy w pętli for
może być również nieco bardziej rozbudowany.
int j = 0; for(int i = 0;i<1 && j<2; ++i) { ++j; System.out.println("i = " + i + ", j = " + j); }
Otrzymamy: i = 0, j = 1
.
Jak wspomniałem na początku. Java oferuje również drugą wersję pętli for
(znanej pod nazwą: enhanced for). Jest ona znacznie prostsza w implementacji, a jej głównym zastosowaniem jest iteracja po kolekcjach. Ważne jest jednak to, że w tej wersji pętli for
nie możemy inicjalizować ani modyfikować tablic, usuwać elementów z kolekcji oraz iterować po wielowymiarowych strukturach. Popatrzmy na przykład.
List<Integer> list = ArrayList<Integer>(); list.add(10); for(int x : list) { System.out.println(x); }
Tak w prosty sposób możemy iterować się po różnego rodzaju kolekcjach. Pamiętajmy jednak, że po lewej stronie wyrażenia w pętli musimy mieć podany typ danych jaki wykorzystywany jest w iterowanej kolekcji. Ja w przykładzie podałem int
, ale jak już pewnie wiesz, z materiału na temat typów danych, nie ma to znaczenia, bo zadziała mechanizm zwany unboxing’iem.
Tworzenie oraz użycie pętli do/while
Pętla do-while
od pętli while
różni się w zasadzie tylko tym, że jej warunek sprawdzany jest na końcu, a nie na jej początku. Oznacza to tyle, że instrukcje umieszczone w ciele pętli do-while
zawsze przynajmniej raz zostaną wykonane.
do { System.out.println("Hello"); } while(false);
Tak samo jak w przypadku pętli while
lub for
w przypadku, kiedy mamy pojedynczą instrukcję umieszczoną w pętli, możemy pominąć nawiasy klamrowe.
do System.out.println("Hello"); while(false);
Porównywanie różnych rodzajów pętli
Zarówno pętla do-while
i while
wykonują zestawy instrukcji do momentu, kiedy warunek logiczny nie osiągnie wartości false
. Jest tylko jedna różnica między nimi. Pętla do-while
wykonuje instrukcje, a dopiero później sprawdza warunek. W pętli while
to warunek jest sprawdzany najpierw.
Uproszczona pętla for
jest znacznie łatwiejsza w użyciu, ale ma mniej zastosowań. Nie może być używana do inicjalizacji tablic, modyfikacji elementów kolekcji oraz ich usuwania. Nie jest możliwe również iterowanie po wielowymiarowych strukturach danych. Rekomendowane jest wykorzystywanie pętli for
, kiedy z góry znamy liczbę iteracji (na przykład w przypadku tablic). W innym przypadku wskazane jest zastosowanie pętli do-while
oraz while
.
Użycie break oraz continue
Słowo kluczowe break
używa się do natychmiastowego przerwania wykonywania pętli lub konstrukcji switch
. Słowo kluczowe continue
jest używane do pominięcia kolejnych instrukcji w danej iteracji pętli i rozpoczęcia kolejnej. Kiedy użyjemy słówko break
lub w pętli zagnieżdżonej to przeskoczymy do pętli nadrzędnej. W przypadku continue
, pominiemy dalsze instrukcje i przejdziemy do kolejnej iteracji w tej samej pętli (lub też przeskoczymy do pętli nadrzędnej, jeśli pominięta iteracja była ostatnia).
loop1: for(int i = 0;i<2;i++) { for(int j = 0;j<3;j++) { if(j == 1) { continue loop1; } System.out.println(i + " " + j); } }
Powyższy kod wypisze na ekranie: 0 0, 1 0
. Instrukcja continue loop1;
w momencie uruchomienia (po spełnieniu warunku logicznego w if
) będzie przeskakiwała do pętli loop1
(na temat etykiet piszę nieco niżej). Gdyby nie używać tutaj etykiety, to continue
po prostu pominęło by drugą iterację w pętli zagnieżdżonej i przeszło do kolejnej. Na wyjściu mieli byśmy wtedy: 0 0, 0 2, 1 0, 1 2
. Instrukcja continue loop1;
w pętli zagnieżdżonej oznacza po prostu „kontynuuj pętlę loop1”.
Gdyby w podanym przykładzie zamiast continue
zastosować słówko break: break loop1;
to spowodowało by to po pierwsze, pominięcie kolejnych instrukcji w pętli zagnieżdżonej oraz zakończenie wykonywania pętli loop1
(instrukcja break loop1;
oznacza „przerwij pętlę loop1”). Użycie samego break
(„przerwij pętlę zagnieżdżoną”) przerywało by jedynie wykonywanie pętli zagnieżdżonej i przejście o poziom wyżej do kolejnej iteracji pętli loop1
. Popatrzmy na krótki przykład.
int count = 0; loop1: for(int i = 0; i<3; i++) { for(int j = 0; j<2 ; j++) { if(j == 1) continue loop1; count++; } } System.out.print(count);
Jakie będzie wyjście takiego programu? Odpowiedź to: 3
. Zauważmy, że pętla zagnieżdżona dla każdej iteracji pętli zewnętrznej będzie wykonywała instrukcję count++;
tylko raz. Przy drugiej próbie za każdym razem zadziała continue loop1;
czyli będziemy przechodzili do pętli loop1
i rozpoczynali kolejną iterację.
Etykiety
Java umożliwia dodawanie etykiet do bloków kodu zdefiniowanych za pomocą nawiasów klamrowych, wszystkich konstrukcji pętli (while
, do-while
, for
, for-each
), konstrukcji warunkowych (if
i switch
), wyrażeń przypisania, instrukcji return oraz bloków try
i instrukcji throw
.
public static void main(String[] args) throws Exception { itIsBlock: { String myLocalVariable = "Hello"; } int a = 0; itIsLoop: for(;a<0;) { } itIsSecondLoop: do { } while(a != 0); itIsNewValue: a = 1; itIsConditionStatements: if(a != 1) { } itIsSwichStatements: switch (a) { } itIsTryBlock: try { itIsThrowInstruction: throw new Exception(); } finally { itIsReturnStatements: return; } }
Witam,zgłębiam Jave i widzę że u ciebie na blogu znajdę dużo cieka0wych rzeczy.
Cześć Monika, korzystaj dowoli :)