Kolekcje
Cześć!
To już trzecia część przygody z Javą! Jak idzie Wam rozpracowywanie warunków, pętli i operatorów? Mamy nadzieję, że powoli nabieracie wprawy, bo dzisiaj zanurkujemy w jeszcze jedno nowe zagadnienie – kolekcje!

Wstęp do kolekcji
Wczoraj wspomnieliśmy, że w naszym projekcie brakuje nam bazy lotów. Celem dzisiejszej lekcji jest utworzenie brakującego elementu. Czym tak naprawdę jest baza lotów? Możemy ją sobie wyobrazić jako “worek” z lotami. W Javie, wszystkie takie “worki” nazywamy kolekcjami. Jedną z typów kolekcji jest ArrayList
, i to właśnie nią zajmiemy się dzisiaj.
Czym jest ArrayList
? Mówiąc w skrócie, jest to obiekt, który może przechowywać inne obiekty. To ważne: ArrayList
przechowuje inne obiekty, ale nie może przechowywać typów prymitywnych takich jak int. Zaraz, zaraz, czy oznacza to, że w ArrayList
nie można przechowywać liczb? Absolutnie nie, chociaż z pozoru tak to może wyglądać. Wszystkie typy prymitywne mają swoje wrappery (obiekty opakowujące dany typ prymitywny). Dla inta, wrapperem jest Integer
. Prawda, że nazwa podobna?
Tworzymy kolekcję
Przejdźmy do tworzenia "worka" z danymi. Składnia jest bardzo podobna, jak w przypadku tworzenia innych obiektów:

Taki sposób jest w 100% poprawny, ale niezalecany. Obiekt zdefiniowany w taki sposób może zawierać w sobie wiele różnych typów, a tego nie chcemy. Tworząc listę zawsze staramy się określać, jaki typ danych będzie przechowywać:

Tak zdefiniowany ArrayList
może przechowywać wyłączenie obiekty typu String – może to być np. lista imion, lista miast itd.
Utworzenie obiektu listy to dopiero początek. Jest to kolekcja, więc warto dodać kilka elementów. W tym celu używamy metody add(String e)
. Spróbujmy więc – otwórz nowy projekt w edytorze i wpisz w nim poniższy kod w klasie Main
:

Uruchom kod i sprawdź, co się stało. Chwila… czyżby pojawił się jakiś błąd?

I owszem! Ale nie ma powodu do paniki, błędy to chleb powszedni każdego developera :) Grunt to umieć odczytać, co komunikat błędu ma nam do powiedzenia. W tym przypadku treść błędu mówi nam, że kompilator nie zna takiej klasy jak ArrayList
. Faktycznie, w kodzie jej nie mamy. Musimy wskazać, gdzie można szukać takiej klasy. Na samej górze kodu dodajemy zatem import, znaleziony w oficjalnej dokumentacji Javy:

Oczywiście importy i pakiety to bardzo szeroki temat i nie będziemy się w niego tu zagłębiać. Wystarczy nam obecna wiedza.
Próbujemy ponownie uruchomić kod po zmianach. Nic się nie wyświetla, więc pół sukcesu – nie ma błędów. Nauczeni doświadczeniem, dodajemy wyświetlanie całego obiektu o nazwie list
.

Dodajmy kilka obiektów do naszej listy:

Pobieranie elementów z kolekcji
Świetnie, nasza lista ma już jakąś zawartość. Wyobraźmy sobie teraz, że chcemy wyświetlić nazwę pierwszego z nich. Jak się do niego dobrać, znając tylko jego pozycję na liście? Wykorzystamy tu jego indeks. Czym jest indeks? Jest to numer reprezentujący pozycję elementu w kolekcji. Indeksy liczymy od zera, czyli pierwszy element będzie miał indeks 0.
Jak zatem pobrać pierwszy element? Do tego służy metoda get(int index)
. Spójrz na jego zastosowanie poniżej:

Przedstawione metoda get(int index)
zwraca element. Wiemy, że będzie to String, ponieważ na samym początku określiliśmy, że ArrayList
będzie przechowywać tylko Stringi. Pamiętasz, jak wspomnieliśmy o znaku =
? Tutaj też znalazł on zastosowanie. Zapis String element = list.get(0);
możemy przeczytać jako: "pobierz element o indeksie 0 z listy o nazwie list
, wynik przypisz do nowej zmiennej typu String o nazwie element
."
Teraz Twoja kolej: spróbuj zmienić numer indeksu w tej metodzie na inny. Poćwicz z różnymi liczbami: zobacz, co się stanie, gdy wpiszesz liczbę ujemną, lub większą niż liczba elementów w kolekcji.
Operacje na elementach kolekcji
Umiemy już pobierać z kolekcji element z danym indeksem. A co by się stało, gdybyśmy dostali zadanie wyświetlenia wszystkich Stringów, których długość jest większa niż 5 znaków. Jak widzisz, mamy tu zadany pewien problem, który trzeba rozwiązać. Zastanówmy się, jak do tego podejść, nie patrząc w kod. Mamy "worek" z wyrazami. Potrzebujemy wypisać tylko te wyrazy, które mają więcej niż 5 znaków. Co robimy? Pobieramy po kolei wszystkie elementy z "worka", sprawdzamy długość każdego z nich, i wyświetlamy dany wyraz, jeśli spełnia określony warunek.
Sposób rozwiązania w kodzie jest analogiczny. Od razu powstaje pytanie: jak przejść po wszystkich elementach listy? Odpowiedź powinna nasunąć Ci się sama: potrzebna nam pętla! Dodaliśmy ją w poniższym kodzie:

W pętli generujemy liczby od 0 do 3 – cyfra 4 nigdy się nie pojawi, ponieważ istnieje warunek i < 4
. Następnie dla każdego i pobieramy element z listy, zapisujemy go do tymczasowej zmiennej i wyświetlamy. Zatrzymajmy się przez chwilę na pojęciu "tymczasowa zmienna". Co ono oznacza? Wszystkie zmienne tworzone wewnątrz pętli istnieją tak długo, dopóki trwa dana iteracja, i "znikają" w kolejnym przebiegu pętli.
Powyższy kod działa… ale tylko wtedy, gdy w kolekcji są cztery elementy. To niedobrze! Wystarczy zmniejszyć lub zwiększyć ich liczbę, by kod przestał funkcjonować prawidłowo. Co możemy z tym zrobić? Czy możemy w jakiś sposób dynamicznie pobrać aktualną liczbę elementów w kolekcji? Oczywiście! Taką wartość zwróci nam metoda size
, zastosowana tak jak poniżej:

Prosty zabieg, a ułatwia tak wiele. Wypróbuj go w swoim kodzie, eksperymentując z różnymi rozmiarami kolekcji!
Idziemy dalej. Kolejny krok to sprawdzenie długości Stringa i decyzja, czy chcemy go wyświetlić. Jak to zrobić? Pomocna będzie metoda length()
, również opisana w dokumentacji.
Kod po modyfikacji:

Sprzątamy kod
Jak widzisz, nasza metoda Main
zawiera już sporo kodu, przez co robi się mało czytelna. Spróbujmy ją uporządkować poprzez utworzenie metody, która wyświetli Stringi dłuższe niż 5 znaków w osobnej metodzie. Brzmi prosto, ale żeby wywołać metodę, potrzebujemy obiektu (nie jest to do końca prawda, ale nie chcemy komplikować). Utwórzmy klasę ComputerItems
, która będzie zawierać listę. Listę wypełnijmy w konstruktorze, a następnie dodajmy metodę, która wyświetli wybrane elementy.

Zmienna o nazwie list
została dodana w klasie – oznacza to, że każdy obiekt klasy ComputerItems
będzie zawierał listę Stringów. W konstruktorze następuje wypełnienie listy elementami. Po wywołaniu metody displayItems()
, w konsoli wypisywane są Stringi, które zawierają więcej niż 5 znaków. Zwróć uwagę, o ile uproszczona została metoda Main
! Staraj się trzymać zasady, że w Main
znajduje się tylko to, co musi tam być – tworzenie niezbędnych obiektów i wywołania metod. Wszystkie pozostały elementy powinny być "pozamykane" w osobnych fragmentach kodu.

Dodajemy bazę lotów
Możemy teraz wrócić do naszej wyszukiwarki lotów. Celem tej części Wyzwania było zdefiniowanie bazy lotów. Zróbmy to! Spróbuj najpierw samodzielnie stworzyć klasę FlightDatabase
. Dodaj w niej pustą listę lotów, a następnie, w konstruktorze, do listy lotów dodaj kilka obiektów.
Spróbuj wykonać to ćwiczenie, a dopiero później porównaj swój kod z przykładowym rozwiązaniem poniżej. Czy pamiętasz, że jeden lot (Flight
) przyjmuje dwa argumenty: lotnisko wylotu i lotnisko przylotu?

To ćwiczenie nie powinno sprawić Ci większych problemów, ale jeżeli rozwiązanie nie jest dla Ciebie jasne na pierwszy rzut oka, poświęć dłuższą chwilę, by je przeanalizować. Podczas nauki bardzo ważne jest, by systematycznie nadbudowywać swoją wiedzę i nie “przeskakiwać” kolejnych tematów – wszystkie braki wyjdą prędzej czy później!
Zadanie domowe
Wykorzystując zdobytą wiedzę, napisz metodę checkIfFlightExists
w klasie FlightDatabase
. Metoda powinna wyświetlić krótką informację, czy lot o podanym lotnisku startowym i końcowym znajduje się w bazie. Czy pamiętasz, w jaki sposób porównujemy ze sobą dwa Stringi?
Powodzenia!