Kurs Java Podstawy - rozszerzony

Pętla for each

Pętla for each to konstrukcja, która pozwala na sekwencyjne przeglądanie różnych zbiorów danych. Mogą nimi być tablice, a także dynamiczne struktury jak na przykład listy. W tym drugim przypadku musimy jednak skorzystać z Iteratora, a ogólniej mówiąc obiekty typu Iterable.

Schematyczna konstrukcja pętli for each wygląda następująco:

for(Typ_Obiektu nazwa_obiektu : nazwa_tablicy){ ... }
lub
for(Typ_Obiektu nazwa_obiektu : kolekcja_implementująca_Iterable){ ... }

Powyższy zapis można traktować dosłownie tak jak nazywa się ten typ pętli "dla każdego elementu z tablicy ...", lub "dla każdego(kolejnego) elementu kolekcji ...". Kolekcje będą jednak omówione dopiero w późniejszym czasie, więc na razie skupmy się na przeglądaniu tablic.

Utwórzmy 10 elementową tablicę liczb typu całkowitoliczbowego i wypełnijmy ją wartościami od 1 do 10. Następnie przy użyciu pętli for each wyświetlmy kolejne liczby i zobaczmy jakie to proste.

public class Test {
	public static void main(String args[]){
		int[] tablica = new int[10];
		//wypełnienie tablicy
		for(int i=0; i<10; i++)
			tablica[i] = i+1;

		//wyświetlenie przy użyciu pętli for each
		for(int x : tablica)
			System.out.println(x);
	}
}

Jak widać pętla for each znacznie upraszcza i sprawia, że kod dzięki temu często może być dużo czytelniejszy. Warto jej używać szczególnie w przypadku tablic z długimi nazwami z oczywistych względów.

<- Poprzednia LekcjaNastępna Lekcja ->

Komentarze

tomek

Przede wszystkim chciałbym pogratulować świetnego kursu, przejrzałem kilka i ten zdecydowanie do mnie przemawia, jest czytelny, wszystko fajnie wytłumaczone, a tego mi brakowało ;)



A teraz a propos kodu:

for(int i=0; i<10; i++)
tablica[i] = i+1;

Czemu tutaj nie ma zamykających i otwierających klamerek? Wiem, że bez nich działa, bo sprawdziłem ale w temacie pętle pokazywałeś pętle for z użyciem klamerki

Slawek

Zacytuję siebie z tematu o pętlach:

Podobnie jak przy instrukcji warunkowej if można pominąć nawiasy klamrowe w wypadku, gdy w ciele pętli znajduje się tylko jedna metoda.

Jeśli pominiesz klamry, to w ciele pętli wykona się tylko instrukcja występująca bezpośrednio po niej. Jeśli użyjesz klamer to możesz tam umieścić na przykład wywołanie kilku metod, wyświetlenie tekstu i jeszcze inne instrukcje sterujące.
Ktoś

Tak na chłopski rozum, klamry mówią "wykonuj w pętli wszystko co jest między klamerkami" a jak ich nie ma to wykona tylko to co znajduje się pod (zaraz po) definicji pętli

DDO

Brak klamer to podstawa nawet w Pascalu nie było znacznikow dla 1 lini :-)

Marcin Kunert

Zasady Clean Code radzą zawsze używać klamerek. Kod jest wtedy zdecydowanie bardziej czytelny.

DDO

Nie w przypadku 1 liniowych
if(parametr)
//zadanie
else if (parametr2)
//zadanie
[...]
else(parametr3)
//zadanie

Pajączek

Jak else, to już bez (parametr) !!!!

mabiki

Ciało pętli to zawsze następna instrukcja. Instrukcje można łączyć w bloki za pośrednictwem nawiasów klamorowych przez co traktowane są przez pętlę jako jedna instrukcja.

Krzysiek

A jak wypenić tabele aby wpisły się w nią liczby w odwrotnej numeracji, nie od 1 do 10 tylko od 10 do 1? Jest to możliwe?

Marcin



Krzysiek:

A jak wypenić tabele aby wpisły się w nią liczby w odwrotnej numeracji, nie od 1 do 10 tylko od 10 do 1? Jest to możliwe?


public class Test {
public static void main(String args[]){
int[] tablica = new int[10];
//wypełnienie tablicy
for(int i=10; i>10; i--)
tablica[i] = i-1;

//wyświetlenie przy użyciu pętli for each
for(int x : tablica)
System.out.println(x);
}
}
Jeśli dobrze myślę, to własnie tak.
Raqs

To nie jest raczej dobrze. W tym wypadku wypełnia tablicę od końca i wstawia liczby też od końca. Można wypełniać jeszcze jedną zmienną, która startuje z długością tablicy(10) i przy każdym kroku zmniejsza się o 1.
---


public class Test {
public static void main(String args[]){
int[] tablica = new int[10];
//wypełnienie tablicy
int j = tablica.length;
for(int i=0; i<10; i++){
tablica[i] = j;
j--;}

//wyświetlenie przy użyciu pętli for each
for(int x : tablica)
System.out.println(x);
}
}

Krzysiek

Teraz działa;) Wygląda na to że bez instrukcji nazwa_tablicy.length to nic z tego by nie wyszło. Ja kombinowałem wcześniej podobnie jak kolega Marcin czyli pętelkowanie w drugą stronę (w dół), ale jak widać nie wszystko jest tu analogicznie logiczne;)))

Krzysiek

Sorry to jeszcze ja. Instrukcja nazwa_tablicy.lenght to tylko odpowiednik długości tablicy czyli liczby jej elementów. Tak wiec mozna intowi przypisać odrazu ilość tych elementów liczbą np. 10, 20, 30 itd. Potrzebujemy tylko dodatkowej zmiennej a nie tej instrukcja chociaż warto o niej wiedzieć. Ale ja głupi jestem. Dzięki

DDO

Ale to i tak nie dokonca dobrze, bo co jak poda tablice >10 ??
Wtedy funkcja nie wypelni wszystkich komurek, zamiast 10 w petli trzeba zastosowac zmienna rowna ilosci pol tablicy
powinno byc

public class Test {
public static void main(String args[]){
int[] tablica = new int[10];
//wypełnienie tablicy
int j,a = tablica.length;
for(int i=0; i<a; i++){
tablica[i] = j;
j–;}

//wyświetlenie przy użyciu pętli for each
for(int x : tablica)
System.out.println(x);
}
}

anonim

to chyba oczywiste, że ta pętla nie działa ponieważ warunek i>10 nie jest już spełniony w momencie jej wykonywania.

int[] tablica = new int[10];
int k=10;
for(int i=tablica.length-1; i>=0; i--){
tablica[tablica.length-k] = i+1;
k--;
}

for(int x : tablica)
System.out.print(x + " ");

V0ldek

for(int x : tablica)

Nie rozumiem co to znaczy. Czemu nie ma średników i co znaczy dwukropek? I dlaczego nie ma modyfikatora licznika?

Witek

Witam
Pętla ta automatycznie przechodzi przez wszystkie elementy tablicy lub też kolekcji od początku do końca.
for( typ zmienna – interacyjna : tablica) zmienna interacyjna czyli w twoim przypadku „x” będzie miał dostęp do kolejnych elementów tablicy(kolekcji).

Np.:

int tab[] = {1,2,3,4,5,6,7,8,9,10};
int suma = 0;

for(int i = 0; i < 10; i++)
suma += tab[i];



To samo tylko z wykorzystaniem rozszerzonej pętli for –each


int tab[] = {1,2,3,4,5,6,7,8,9,10};
int suma = 0;


for(int x : tab)
suma += x;


Jak widać jest dużo prościej i nie musisz się obawiać o przekroczenie rozmiaru tablicy

Kelzaer


for(int x : tablica)

Nie rozumiem co to znaczy. Czemu nie ma średników i co znaczy dwukropek? I dlaczego nie ma modyfikatora licznika?


Przeczytaj jeszcze raz wstęp do tego rozdziału. Szczególnie tę część:

Schematyczna konstrukcja pętli for each wygląda następująco:

for(Typ_Obiektu nazwa_obiektu : nazwa_tablicy){ ... }



W opisanym zadaniu mamy:
"Typ_Obiektu nazwa_obiektu" ="int x" następnie dwukropek ":" i na koniec "nazwa_tablicy" = "tablica"

Mogło zmylić Cię to, że pętla for each zaczyna się tak jako zwykły for - for(... Jednak po zawartości między nawiasami można wywnioskować, że jest to właśnie for each :)

Witek

Witam
Poniżej masz program, który przedstawia to zagadnienie, mam nadzieję, że pomoże zrozumieć działanie tej pętli.

import java.util.Scanner;


public class Tablica_Jednowymiarowa {

/**
* Napisz program, który pobiera od użytkowniak rozmiar n-elementowej tablicy
* jednowymiarowej, następnie umieszcza liczby od 0 do n w tablicy o nazwie tab.
* Zawartość wyświetli na konsoli
*
* @author Wiciu
*
*/
public static void main(String[] args) {

int n;
int tab[];
Scanner odczyt;

System.out.println("Podaj rozmiar tablicy: ");

odczyt = new Scanner(System.in);

n = odczyt.nextInt();

tab = new int[n];

for(int i = 0; i < n; i++)
tab[i] = i;


for (int x : tab) {

System.out.println("dane[" + x + "] = " + x);
}


}

}

Dawid

Czy ta pętla jest konieczna tylko względem prostszego pisania kodów czy jest ważna do nauczenia się?
Bo ja tej for each nie kumam.

Slawek

To tylko ficzer, spokojnie możesz używać zwykłej for, albo while. Po pewnym czasie wróć sobie do niej to wszystko będzie jasne.

gandy

Czy w javie istnieje możliwość iteracji elementów tablicy od innej wartości początkowej czy też zawsze do pierwszego elementu tablicy musimy się odwoływać jako do elementu zerowego?

Slawek

Wykorzystując zwykłą pętlę for, a nie for each możesz iterować od którego elementu Ci się podoba.

gandy

Nie o to mi chodziło. Tylko o to czy możemy w jakiś sposób inaczej indeksować elementy tablicy o nazwie np. tab tzn nie element 0,1,2,3,4,5 ( dla sześciu elementów) tylko np element 4,5,6,7,8,9(dla takiej samej ilości) , a później odwoływać się do poszczególnych elementów przez tab[4],tab[5],tab[6],tab[7],tab[8],tab[9]. Czy też iteracja pierwszego elementu jako zerowego jest niemożliwa do obejścia?

Slawek

Nie da się tego obejść, musiałbyś stworzyć własną klasę i opakować tablicę - ale nie ma to większego sensu.

gandy

Dzięki.

jestęsobą

Bardzo dobry kurs. Doceniam to, że chcesz pomóc, lecz staraj się pisać językiem prostszym(takim, żeby rozumieli go tacy jak ja- czyli ci, którzy po raz pierwszy się z Javą stykają.
Pozdrawiam :)

Adrian

A gdzie jest treść zadania do wykonania?

Fryta

Witam,
troche już programuje (od 2 technikum do teraz, czyli 7 lat, niestety jako samouk) ale w JAVA siedze dopiero drugi dzień. Odnośnie wypełnienia tablicy od 10 do 1 to można zrealizować następująco:
for (int i=0 ; i<10 ; i++)
tablica[i] = 10-i;
Wiem, że odpowiedź jest troche późna (po roku) ale może się komuś przyda.
Jeśli chodzi o kurs to miewam pewne problemy przy polimorfizmie, klasach abstrakcyjnych i interfejsie. Reszta kursu jest świetna. Sławku, czy ty piszesz aplikacje na androida i czy jesteś może na Google Play? Chciałbym poznać kilka twoich projektów - podejrzewam, że bardzo rozbudowane i dopracowane skoro Jave masz w małym paluszku. Jakbyś mógł pochwalić się swoim dorobkiem byłbym wdzięczny:D

Max



jestęsobą:

Bardzo dobry kurs. Doceniam to, że chcesz pomóc, lecz staraj się pisać językiem prostszym(takim, żeby rozumieli go tacy jak ja- czyli ci, którzy po raz pierwszy się z Javą stykają.
Pozdrawiam


Eee, kurs jest baaardzo przystępnie napisany, zaczynałem bez żadnego pojęcia od pierwszej lekcji. Najlepszym sposobem nauki dla mnie jest przeczytanie lekcji, zrobienie zadań i przeczytanie i analiza wszystkich komentarzy, szczególnie tych zawierających kody programu. Skończyłem właśnie kurs podstawowy i będę zaczynał programowanie obiektowe. Na razie jeszcze nie wiem czym się zajmują programiści w dużych korpo, zarabiający 200k rocznie, bo przypuszczam że nie wypełniają tablic żeby wybuchła bomba albo kalkulator BMI . Ale nie zawracam sobie tym głowy. Podobnie nie zawracałem sobie głowy tym, że nie mam logicznego obrazowego pojęcia klasy, przyjmuję to póki co jako warunek konieczny do dalszego pisania programu.

Na marginesie fajna zabawa z tym programowaniem, taki język matematyczny, symboliczny ;)
Dcortez

Jak powinno się definiować tym w pętli foreach gdy nasza kolekcja jest definiowana interfejsem, np.
List workers = new ArrayList;
Klasa zawierająca ową kolekcje implementuje Iterable i ma napisany iterator. W tym wypadku rozumiem że jak przykładowy x zostanie stworzony właśnie ten iterator, a nie instancja klasy?

Sławek Ludwiczak

To, czy referencja kolekcji będzie interfejsem, czy konkretną klasą nie ma znaczenia (przy czym należy pamiętać, że wywoływana jest metoda na rzecz obiektu, nie typu referencji i że nie mamy wtedy dostępu do wszystkich metod klasy bez rzutowania). Pętla foreach wygląda dokładnie tak samo niezależnie od sposobu utworzenia kolekcji.
Nie do końca też rozumiem Twoje pytanie, więc jeśli możesz to odpisz czy o to chodziło:
Zmienna x nie jest iteratorem, jest to zmienna konkretnego typu, która stanowi kopię referencji do danego obiektu udostępnianego przez iterator.
Do x w kolejnych iteracjach przypisywana jest wartość z wywołania meotdy next() iteratora danej kolekcji.
Inaczej pętlę for each można by zapisać jako:


for(Iterator<TypObiektu> i = collection.iterator(); i.hasNext(); ) {
TypObiektu item = i.next();
Działania na referencji item będącej kopią oryginalnej referencji
}


Bardzo ważne jest to, że w pętli for each nie działamy na oryginalnych referencjach, a na ich kopii. Do kolejnych elementów kolekcji nie przypiszemy więc za jej pomocą nowych obiektów i jest to często spotykany błąd. For each'a bezpieczniej jest używać tylko do odczytu.
Dcortez

Dzięki za odpowiedź. Widze że niestety ucieło mi troche poprzedniego komentarza albo nie uważnie go wpisałem. Mianowicie byłem ciekawy czy gdy użyję generycznej listy inicjowanej nie tyle konkretną klasą a interfejsem, przypuśćmy IEmployee, to moge potem iterować po tej kolekcji w ten sposób:

for ( IEmployee x: workers) {
do sth..
}

Zastanawiałem się czy będzie to składniowo poprawne, ale jeśli dobrze rozumiem sprawi to że wywoła się odpowiedni iterator a z x w kolejnych przejsciach beda podstawiane odpowiednie elementy z kolekcji?

Sławek Ludwiczak

Jeśli chodzi Ci o coś takiego:


List<IEmploye> lista = new ArrayList<IEmploye>();
for(IEmploye e: lista)
sth...

to oczywiście, że można tak robić.
Jeśli określisz typ listy jako konkretny typ, a nie interfejs, np:

List<Employe> lista = new ArrayList<Employe>();

to możesz po niej iterować w identyczny sposób jak powyżej, zakładając, że Employe jest klasą implementującą interfejs IEmploye.
Rafal

Nie rozumie tego fragmentu kodu
Tablica [i] =i + 1
W sumie chodzi mi tylko o tablica [i]

Witek

Witam
Jak masz w pętli for i = 1 to do " i " dodawana jest za kazdym razem wartość 1
np:
for(int i = 1; i < 10; i++)
Tablica[i] = 1 + 1; // pierwsza 1-ka to nasze " i "
To tak najprościej.
Pozdrawiam

Rafał

dobrze, ale musimy ten fragment dopisać??
dlaczego zapisaliśmy tablica [i] ja maszyna, to rozumie ??

Witek

Witam
Nie musimy tego „ i + 1 „ wpisywać, możemy wpisać samo „ i „. Najlepiej jak sobie to przećwiczysz na różne sposoby te „ i + np.: 1 czy 2 itd. „.
Pozdrawiam.

SAK

Nie mogę coś zastosować For each w przypadku 3-wymiarowej tablicy. Można przykład?

Marcin Kunert

MojObiekt[][][] obiekty3 = new MojObiekt[5][5][5];

for(MojObiekt[][] obiekty2 : obiekty3) {
for(MojObiekt[] obiekty1 : obiekty2 ) {
for(MojObiekt obiekt : obiekty1) {
System.out.println(obiekt.toString());
}
}
}

Sorry za formatowanie, ale pewnie rozkminiasz ideę.

placek

Przy tej tablicy:

ZLE:
new MojObjekt[5][5][5];
POPRAWNIE:
new MojObjekt[5][][];

SAK

Dzięki bardzo!
Właśnie coś podobnego kombinowałem, ale nie wiedziałem jak to dokładnie zapisać.. :)

PanKitke

Napotkałem następujący problem. Chcę, żeby ilość pól w tabeli była uzależniona od danych wprowadzonych przez użytkownika.
Zapis:
int ilePol;
int[] tablica = new int[ilePol]; grzecznie się kompiluje, jednak przy probie wypełnienia tablicy przy użyciu pętli pojawia się błąd java.lang.ArrayIndexOutOfBoundsException: 10.

PS Czy w Javie jest (pewnie jest ;-)) odpowiednik var_dump z PHP?

PanKitke

OK... odpowiedź znajduje się w następnej lekcji... :-)

Marcin Kunert

Podejrzewam, że zarezerwowałeś miejsce na 10 liczb. Pamiętaj jednak o tym, że numerowane jest od 0.
tablica ma elementy od tablica[0] do tablica[9].
Co do var_dump - nie spotkałem się, ale debugowanie w Javie rozwiązuje ten problem. Ustawiasz sobie breakpointa w momencie gdzie chcesz sprawdzić obiekt, zaznaczasz go i naciskasz Ctrl+Shift+I (Inspect chyba)

k2marko

Może ktoś wytłumaczyć dlaczego w tym przypadku w metodzie main kwadratowe nawiasy są dopiero po "args" a nie, tak jak zwykle, po "String"? Czy ma to jakiekolwiek znaczenie? Pytam bo nigdy wcześniej się z tym nie spotkałem. Nigdy to znaczy od początku tego kursu :)

Sławek Ludwiczak

Oba sposoby zapisu są poprawne i są sobie równoznaczne. Możesz dać kwadratowe nawiasy przy typie, albo przy zmiennej.

snt.banzai

Starałem się zrobić zadanie z poprzedniej lekcji (to z bombą) przy użyciu pętli for each i już jestem całkiem blisko:) Problem w tym, że program prowadzi odliczanie od zera w górę :D czy ktoś może pomóc jak temu zaradzić? Mój kod poniżej:

import java.util.Scanner;

public class NiefartSapera4{
public static void main(String[] args){
System.out.println("Podaj liczbę sekund pozostałych do wybuchu bomby:");
Scanner czas = new Scanner(System.in);
int left = czas.nextInt();

int[] tablica = new int[left+1];

for(int i = left; i>0; i--){
tablica[i] = i-1;
}

for(int x: tablica){
System.out.print("Do wybuchu bomby pozostało "+x+" sekund! \r");
try { Thread.sleep(1000); }
catch(Exception e) {}
}
System.out.print("Nastąpiła detonacja, bye, bye! ");
}
}