SCJP - Pętle
Rozdział piąty Książki – patrz artykuł "Przygotowania do SCJP czas zacząć" – wydawał się być bardzo długi a przez to straszny, ale nie jest źle. Idzie dosyć szybko, a to pewnie z tego powodu, że poruszane w nim tematy nie są przesadnie skomplikowane. Dziś będzie właśnie jeden z nich, tj. pętle. Pętle ‘while’ i ‘do while’ nie zaskakują zupełnie niczym, więc nic o nich nie będę pisał. Przejdę od razu do pętli ‘for’, oraz instrukcji ‘break’ i ‘continue’.
Pętla ‘for’ występuje w dwu odmianach: tradycyjnej, ogólnego przeznaczenia i nowej – obecnej od Javy 1.5 – służącej do iteracji po elementach kolekcji lub tablicy. O drugim wariancie będę pisał w osobnym artykule, kiedyś w przyszłości. Dziś zobaczmy tylko prosty przykład, jak taka pętla wygląda. Aby więc wykonać jakąś akcję dla kolejnych elementów tablicy, w tym przypadku dla tablicy liczb typu ‘int’, napiszemy
Pętla ‘for’ występuje w dwu odmianach: tradycyjnej, ogólnego przeznaczenia i nowej – obecnej od Javy 1.5 – służącej do iteracji po elementach kolekcji lub tablicy. O drugim wariancie będę pisał w osobnym artykule, kiedyś w przyszłości. Dziś zobaczmy tylko prosty przykład, jak taka pętla wygląda. Aby więc wykonać jakąś akcję dla kolejnych elementów tablicy, w tym przypadku dla tablicy liczb typu ‘int’, napiszemy
for (int i : new int[] { 1, 3, 5, 7, 11 }) {
System.out.print(i + " ");
}
Generalnie, przed dwukropkiem deklarujemy zmienną, do której będą przypisywane kolejne wartości z kolekcji, a po dwukropku umieszczamy dowolne wyrażenie, którego wartością jest kolekcja lub tablica. W naszym wypadku jest to instrukcja tworząca nową tablicę, ale może to być także wywołanie metody czy zmienna referencyjna odnosząca się do obiektu pewnej kolekcji, np.
public static void main(String[] args) {
List<Integer> someList = new LinkedList<Integer>();
someList.add(1);
someList.add(2);
someList.add(3);
for (int i : someList) {
System.out.print(i + " ");
}
}
Tradycyjna pętla ‘for’ składa się z trzech, oddzielonych średnikami sekcji w części sterującej pętli, oraz oczywiście z kodu umieszczonego w tejże pętli. Przyjęło się mówić, że pierwsza sekcja służy do deklaracji zmiennych, druga to warunek pętli, a więc wyrażenie o typie ‘boolean’ a trzecia to instrukcja inkrementacji zmiennych. Co prawda tak się tego zazwyczaj używa, ale trzeba wiedzieć, że pierwsza i trzecia sekcja może zawierać dowolną instrukcję. Sekcja druga to dowolne wyrażenie typu ‘boolean’. Dodatkowo, każda z sekcji może być zupełnie pusta. Przykładowo, poprawną jest pętla
for (System.out.print("sekcja pierwsza") ; ; System.out.print("sekcja trzecia")) {
break;
}
Jeśli druga sekcja jest pusta, to pętla zachowuje się tak, jakby był tam umieszczony literał ‘true’, a więc jest to pętla nieskończona, a właściwie to byłaby to pętla nieskończona, gdyby nie instrukcja ‘break’, umieszczona w ciele pętli. Sekcja pierwsza jest to kod, który uruchamiany jest na samym początku, zawsze dokładnie raz, niezależnie od wartości sekcji drugiej. Przykładowo, uruchomienie programu
public static void main(String[] args) {
int x = 1, y = x;
for (System.out.print("sekcja pierwsza") ; x != y ;
System.out.print("sekcja trzecia")) {
System.out.print("ciało pętli");
}
}
spowoduje wyświetlenie napisu
sekcja pierwsza
Po wykonaniu sekcji pierwszej, przeznaczonej na inicjalizację zmiennych sterujących pętli, obliczana jest wartość wyrażenia z sekcji drugiej; jeśli jest to ‘true’, to wykonuje się ciało pętli. Na końcu wykonywany jest kod z sekcji trzeciej nagłówka pętli, przeznaczonej na kod inkrementujący wartości zmiennych sterujących.
Instrukcja ‘break’ służy do natychmiastowego przerwania wykonania pętli – sterowanie zostaje wówczas przeniesione do pierwszej instrukcji za pętlą. Efektem uruchomienia programu
public static void main(String[] args) {
int i = 0;
for (System.out.println("sekcja pierwsza") ; condition() ;
System.out.println("sekcja trzecia")) {
System.out.println("ciało pętli");
if(i++ > 0)
break;
}
System.out.println("po pętli");
}
public static boolean condition() {
System.out.println("sekcja druga (true)");
return true;
}
jest wyświetlenie sekwencji
sekcja pierwsza
sekcja druga (true)
ciało pętli
sekcja trzecia
sekcja druga (true)
ciało pętli
po pętli
Instrukcja ‘continue’ przerywa bieżące wykonanie pętli, a więc tylko wykonanie ciała pętli w bieżącej iteracji. Zwróćmy uwagę, że instrukcja ‘continue’ nie powoduje zaniechania wykonania trzeciej sekcji nagłówka pętli. Efektem uruchomienia programu
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
if (i % 2 == 1)
continue;
System.out.println(i + " jest liczbą parzystą");
}
}
jest wyświetlenie sekwencji
0 jest liczbą parzystą
2 jest liczbą parzystą
4 jest liczbą parzystą
6 jest liczbą parzystą
8 jest liczbą parzystą
Instrukcje ‘break’ i ‘continue’ odnoszą się domyślnie do pętli, bezpośrednio w której się znajdują, ale możliwe jest wskazanie, poprzez etykietę, pętli bardziej zewnętrznej. Przykładowo, uruchomienie programu
public static void main(String[] args) {
outerLoop: for (int j = 0;; j += 100) {
for (int i = 0; i < 5; i++) {
if ((i + j) % 2 == 1)
continue;
if (j > 100)
break outerLoop;
System.out.println(i + j + " jest liczbą parzystą");
}
}
}
skutkuje wyświetleniem sekwencji
0 jest liczbą parzystą
2 jest liczbą parzystą
4 jest liczbą parzystą
100 jest liczbą parzystą
102 jest liczbą parzystą
104 jest liczbą parzystą
1 komentarz:
Ponieważ w artykule wykorzystano przykład operatora modulo dla badania parzystości od razu przypomniał mi się pierwszy puzzle z książki "Java Puzzlers" (Joshua Bloch)
public static boolean isOdd(int i) {
return i % 2 == 1;
}
Czy ta metoda zawsze zwróci poprawną wartość dla liczby nieparzystej??
Otóż nie! Dla liczby ujemnych nie będzie działać poprawnie ponieważ w skrócie znak wyniku operatora modulo zależy od znaku lewego argumentu.
cytując za książką
This is a consequence of the definition of Java's remainder operator (%). It is defined to satisfy the following identity for all int values a and all nonzero int values b:
(a / b) * b + (a % b) == a
In other words, if you divide a by b, multiply the result by b, and add the remainder, you are back where you started [JLS 15.17.3]. This identity makes perfect sense, but in combination with Java's truncating integer division operator [JLS 15.17.2], it implies that when the remainder operation returns a nonzero result, it has the same sign as its left operand.
Prześlij komentarz