W poprzedniej lekcji utworzyliśmy kolekcję elementów, a następnie dodaliśmy do niej naszego nowo utworzonego kota. Dzisiaj poznamy kilka sposobów, jak odczytać informacje zapisane w kolekcji lub jak z kolekcjami pracować.
Kolekcje to jak już zostało powiedziane jeden z najwazniejszych elementów języka Java. Dlatego tak ważne jest ich dobre poznanie, a także oswojenie się z operacjami które możemy na nich wykonać.
Lekcja
Dzisiejsza lekcja będzie obejmowała dwa główne elementy — pętlę for oraz korzystanie z kolekcji typu lista (będziemy w przykładach uzywać ArrayListy jako przykładu, ale pamiętaj, że istnieją też inne listy — wszystkie je znajdziesz w dokumentacji Oracle, w sekcji ‘all known implementations’ dla interfejsu List). Zaczniemy od samych kolekcji a następnie omówimy pętlę for i jak używać jej z kolekcjami.
Kolekcje typu lista (java.util.List)
Lista to najczęściej spotykana kolekcja w pracy z językiem Java. Nie zgłębiając szczegółów technicznych, listę można opisać jako uporządkowany zbiór elementów, w którym możemy użyć dowolnego z elementów (zarówno tego, który jest pierwszy, jak i ostatni, ale też każdego pomiędzy nimi), możemy dodawać nowe elementy (bez limitu rozmiaru — przynajmniej teoretycznie) i usuwać te już istniejące. Możemy ją porównać do nieskończenie długiej szuflady w jakiejś szafce na dokumenty z amerykańskich filmów, w której mamy teczki z jakimiś dokumentami — te teczki to nasze elementy, a szuflada to kolekcja. Możemy wyciągnąć dowolną z teczek, i wiemy ile ich jest.
Listy w Javie (jak wszystkie kolekcje) są parametryzowane, tzn. musimy wcześniej zadeklarować jakiego typu obiekty będziemy trzymali w tej liście. Możemy oczywiście powiedzieć ‘dowolny’ (jako typ wpisując Object) ale raczej tego nie chcemy. W poprzednim zadaniu używaliśmy listy kotów, w przykładach w dzisiejszej lekcji będziemy używać ciągów znaków — przykłady dzięki temu będą krótsze ;) Parametr kolekcji — oznaczany w dokumentacji jako E — to w naszym przypadku będzie String.
Do tej pory używaliśmy już operacji dodawania na listę, ale usystematyzujemy sobie naszą wiedzę o listach i omówimy najważniejsze metody. Oczywiście metod jest dużo więcej, z niektórych będzie potrzeba skorzystać bardzo szybko, innych nie będziesz używała bardzo długo. Dlatego potraktuj to jedynie jako zachęte do zapoznania się z dokumentacją (link znajdziesz w tytule tego akapitu).
add (E element) — dodaj nowy element do kolekcji
Dodaje nowy element do kolekcji. Po tej operacji długość listy zwiększa się o jeden, nowo dodany element trafia na jej koniec. To najczęstszy sposób dodawania elementów do listy.
List<String> lista = new ArrayList<String>(); //lista pusta
lista.add("pierwszy"); //lista ma jeden element
lista.add("inny"); //lista ma dwa elementy
lista.add("pierwszy"); //lista ma trzy elementy
addAll (Collection<E> elementy) — dodaj wiele elementów do kolekcji
Ta metoda pozwala za jednym razem dodać wiele elementów (nie używając pętli, w zależności od typu listy, operacja ta powinna być co najmniej tak szybka jak dodawanie w pętli, najczęściej jest dużo szybsza, co ma znaczenie w przypadku, kiedy mamy naprawdę dużo elementów). Podobnie jak w poprzedniej metodzie, elementy trafiają na koniec listy, są dodawane w kolejności, w jakiej kolekcja elementy je zwraca — w poniższym przykładzie listy będą po prostu połaczone a kolejność elementów zostanie w nich zachowana.
List<String> listaMoja = ... ; //jakaś lista z 5 elementami
List<String> listaCzyjas = ... ; //jakaś lista z 7 elementami
List<String> lista = new ArrayList<String>(); //lista pusta
lista.addAll(listaCzyjas); //lista ma 7 elementów
lista.addAll(listaMoja); //lista ma 12 elementów
get (int indeks) — pobierz element kolekcji
Pozwala pobrać element z dowolnej pozycji na liście, przy czym pierwsza pozycja ma indeks 0 (jak wszędzie w Javie). Jeśli wskazana pozycja jest większa niż ilość elementów na liście, otrzymamy wyjątek (IndexOutOfBoundException). Pamiętajmy, że nawet usuwając element ze środka, lista pozostaje ciągła (tzn. element z kolejnej pozycji zostaje umieszony na pozycji z której usuwamy).
List<String> lista = new ArrayList<String>(); //lista pusta
lista.add("pierwszy"); //lista ma jeden element
lista.add("inny"); //lista ma dwa elementy
lista.add("pierwszy"); //lista ma trzy elementy
String poczatek = lista.get(0); // zwraca "pierwszy"
String koniec = lista.get(2); // zwraca "pierwszy"
String srodek = lista.get(1); // zwraca "inny"
remove(int indeks) — usuń element z określonej pozycji
Metoda ta pozwala usunąć dowolny element z dowolnej pozycji na liście. Jeśli element nie istnieje, możemy otrzymać wyjątek taki sam, jak w przypadku pobierania elementu z listy (możemy także w pewnych szczególnych przypadkach otrzymać wyjątek UnsupportedOperationException, jeśli użyty przez nas typ listy nie pozwala usuwać elementów, ale jest to bardzo rzadka sytuacja). Metoda ta poza usunięciem elementu z listy, zwraca go (możemy więc coś z nim dalej robić)
List<String> lista = new ArrayList<String>(); //lista pusta
lista.add("pierwszy"); //lista ma jeden element
lista.add("inny"); //lista ma dwa elementy
lista.add("pierwszy"); //lista ma trzy elementy
String poczatek = lista.get(0); // zwraca "pierwszy"
poczatek = lista.remove(0); //zwraca "pierwszy"
String srodek = lista.get(0); // zwraca "inny"
String koniec = lista.get(1); // zwraca "pierwszy"
isEmpty() — sprawdź, czy na liście są jakies elementy
Metoda zwraca wartość prawda/fałsz, która mówi nam czy lista jest pusta (tzn. czy ma dokładnie zero elementów).
List<String> lista = new ArrayList<String>(); //lista pusta
boolean pusta = lista.isEmpty() //zwraca true
lista.add("pierwszy"); //lista ma jeden element
pusta = lista.isEmpty() //zwraca false
lista.remove(0); //zwraca "pierwszy", lista ma 0 elementów
pusta = lista.isEmpty() //zwraca true
size() — sprawdź, ile elementów jest na liście
Metoda ta pozwala na sprawdzenie ile elementów znajduje się na liście. Jest ona szczególnie użyteczna, jesli wczytujemy od użytkownika np. numer elementu z listy i chcemy się upewnić, że użytkownik podał prawidłową wartość (dzięki czemu unikniemy konieczności obsługiwania wyjątków w naszym kodzie).
List<String> lista = new ArrayList<String>(); //lista pusta
int ilosc = lista.size(); //zwraca 0
lista.add("pierwszy"); //lista ma jeden element
ilosc = lista.size(); //zwraca 1
lista.add("inny"); //lista ma dwa elementy
ilosc = lista.size(); //zwraca 2
lista.add("pierwszy"); //lista ma trzy elementy
ilosc = lista.size(); //zwraca 3
lista.remove(0); //zwraca "pierwszy"
ilosc = lista.size(); //zwraca 2
Pętla for
W Javie istnieją dwa warianty pętli for (nazwy zmyślone, na potrzeby rozróżniania w ramach tej lekcji, nie są to oficjalne nazwy obu wariantów!) — pierwszy, normalny (‘standardowy’) oraz drugi, iteracyjny (w innych językach często pętla ta ma nazwę foreach, za chwilę dowiesz się dlaczego; czasem można spotkać się z nazwą ‘for-each’).
Standardowa pętla for
Standardowa pętla for ma w języku Java następującą składnię:
for (inicjalizacja; warunek_zakończenia; krok) {
//to, co chcemy w pętli robić
}
W pierwszej części mamy następujące elementy (każdy z nich jest opcjonalny):
- inicjalizacja — ten fragment wykona się tylko raz, przed pierwszą iteracją. Można tutaj np. zainicjować zmienną pomocniczą, której będziemy używali do tego, żeby pętla wykonała się dokładnie X razy
- warunek_zakończenia — ten fragment jest wykonywany przed każda iteracją. Jeśli warunek tutaj opisany jest spełniony, to następna iteracja się wykonuje, jeśli nie to pętla kończy działanie
- krok — ten fragment wykonuje się po każdej iteracji, używamy go np. do inkrementowania (powiększenia o jeden) licznika iteracji
Przykładowa pętla for wygląda następująco:
for (int i = 0; i<10; i++) {
System.out.println("Iteracja: " + i);
}
Powyższa pętla wypisze dziesięć linijek. Nasza pętla for:
- przed iteracjami inicjalizujemy zmienną i i nadajemy jej wartość 0
- przed każdą iteracją, sprawdzamy czy i jest mniejsze od 10 (zwróć uwagę, że sprawdzamy czy jest mniejsza od 10 — czyli dla i==10, pętla zostanie przerwana; zmienna i jest inicjowana wartością 0, więc zanim pętla będzie przerwana, wykona się 10 iteracji: 0,1,2,3,4,5,6,7,8,9)
- po każdej iteracji powiększamy i o 1 (i++ oznacza powiększ i o jeden)
- w każdej iteracji wypisujemy do konsoli tekst “Iteracja X” (gdzie X to kolejno 0, 1, 2 itd)
Możemy oczywiście pominąć wszystkie te elementy, otrzymując tym samym pętle nieskończoną:
for (;;;) {
//tutaj będziemy potrzebować warunku, który przerwie pętlę instrukcją sterującą break, albo zakończy działanie aplikacji
}
Pętle najczęściej używane są w połączeniu z jakąś kolekcją — np. w sytuacji kiedy chcemy wykonać operację na każdym z elementów, albo np. zsumować pola wszystkich elementów w jedną wartość. Wracając bliżej aplikacji, którą piszemy, fragment kodu który sumuje masę kotów w naszej bazie danych mógłby wyglądać następująco:
Float masa = 0.0f;
List<Kot> koty = ... ; //tutaj inicjujemy i wypełniamy listę
for (int i = 0; i<koty.size(); i++) {
Kot kot = koty.get(i);
masa += kot.getMasa();
}
System.out.println("Wszystkie koty razem ważą w sumie: " + masa);
Jak widać jest to mało wygodne (dużo niepotrzebnego pisania), zobaczmy więc, jak możemy to uprościć.
Pętla for-each
Ta konstrukcja jest w mojej subiektywnej opinii jest bardziej użyteczna od przedstawionej powyżej, choć oczywiście zależy to od sytuacji i należy dobrze znać obie. Konstrukcja ta jest ściśle powiązana z kolekcjami, ponieważ przechodzi ona po każdym elemencie kolekcji (jest to skrót od powyższego kodu). Zaletą jest to, że w przypadku kolekcji nieuporządkowanych (np. Set, Map w ogólnym przypadku), nie musimy samodzielnie obsługiwać iteratora (sposób na przejście po wszystkich elementach kolekcji, także nieuporządkowanej). Przedstawiony na końcu poprzedniej sekcji przykład można zapisac jako:
List<Kot> koty = ... ; //tutaj inicjujemy i wypełniamy listę
for (Kot kot : koty) {
masa += kot.getMasa();
}
Jak widać, sam zapis jest bardziej czytelny i łatwiejszy do pracy z nim. Ogólna konstrukcja wygląda następująco:
Collection<E> collection = ... ; //tutaj jakaś kolekcja
for (E element : collection) {
//działania na obiekcie
}
Poza słowem kluczowym for deklarujemy jakiego typu ma być element podbrany w każdej iteracji, jak ma się in nazywać, a także jak nazywa się kolekcja, z której będziemy pobierać elementy.
Zadanie
Dzisiejsze zadanie będzie nieco bardziej skomplikowane od poprzednich. Ale w końcu to już 6 lekcja ;) Dzisiaj przerobimy zupełnie nasz program tak, aby był on już funkcjonalny. Dla ułatwienia na końcu zadania znajdziesz diagram aktywności (jak z niego skorzystać, przeczytasz we wpisie Ani).
Zadanie polega na przerobieniu programu tak, aby na początku wyświetlało się menu (najprostsze: najpierw wypisz wszystkie dostępne opcje, a następnie wczytaj od użytkownika jego wybór), w którym będziemy mogli wybrać jedną z opcji: dodaj kota, pokaż koty, zamknij program (wpisując odpowiednio 1, 2 lub x z klawiatury). Po wykonaniu danej czynności, wracamy do początku, czyli pokazujemy menu.
Pierwsza opcja, dodaj kota, powinna wywoływać ten kod, który napisaliśmy do tej pory. Czyli wczytujemy po kolei wszystkie dane kota, po czym dodajemy go do kolekcji poprzez nasz obiekt kotDAO.
Druga opcja najpierw wyświetli wszystkie koty (tylko imiona) oraz ich identyfikatory (numer kolejny). Użytkownik powinien wpisać numer w konsoli, po czym wybrany kot się przedstawia lub wyświetlamy informacje o błędzie, że takiego kota nie ma.
Trzecia opcja od razu zamyka program.
Diagram aktywności do tego zadania znajdziesz poniżej. Krok oznaczony innym kolorem to ten fragment, który już zrobiliśmy w poprzednich lekcjach.
Podpowiedzi
Jeśli uważasz powyższą lekcję za przydatną, mamy małą prośbę: polub nasz fanpage. Dzięki temu będziesz zawsze na bieżąco z nowymi treściami na blogu ( i oczywiście, z nowymi częściami kursu Javy). Dzięki!