W ramach kursu swego czasu stworzyliśmy rozdział
Tablice obiektów i typów prostych.
Napisaliśmy wtedy, że tablice służą do przechowywania wielu elementów w ramach jednego obiektu. W ten sposób możemy na przykład
grupowo przechowywać lub transportować obiekty tego samego typu. Zresztą wygląda to tak samo jak w naszym życiu codziennym.
Nikt nie przynosi do domu po jednym obiekcie ziemniaka, tylko zwykle wkładamy je do siatki i przynosimy wszystkie naraz.
Oczywiście w przypadku siatki nie ma znaczenia kolejność ziemniaków, co jest akurat dosyć istotnym elementem w przypadku tablic.
Niemniej można powiedzieć, że takie zbieranie obiektów w grupy jest bardzo praktycznym podejściem i niezwykle często używanym
w programowaniu.
Problem z tablicami
Tablice mają jednak swoje poważne mankamenty i raczej nie są obecnie popularnym sposobem na przechowywanie danych w Javie.
Podstawowym problemem jest potrzeba znajomości rozmiaru tablicy już w trakcie jej tworzenia, przez co niemożliwe jest dynamiczne dokładanie do niej
elementów bez tworzenia
kopii tablicy o rozmiarze większym niż poprzednia tablica.
Problemów z tablicami jest nieco więcej. Na szczęście Java dostarcza nam inne rozwiązanie, które jest wygodniejsze i bardziej eleganckie w użyciu, o którym piszemy już w kolejnym paragrafie.
Kolekcje
Temat ten jest niezwykle ważny i absolutnie każdy programista Javy musi dobrze go poznać.
Samo grupowe przechowywanie danych to tak naprawdę tylko zajawka tego co dają nam kolekcje. W zależności od rodzaju kolekcji
bardzo istotne są zagadnienia takie jak choćby kolejność lub unikalność jej elementów.
Zacznijmy jednak od początku.
W Javie istnieje pakiet o nazwie
java.util, w którym znajduje się interfejs
Collection.
Interfejs ten jest nadrzędnym interfejsem wszystkich rodzajów kolekcji, ale out of the box nie jest implementowany przez żadną klasę JDK.
Dopiero interfejsy podrzędne (dziedziczące) posiadają wbudowane klasy, dostarczające implementacje metod. W celu łatwiejszego zrozumienia najlepiej jest
zerknąć na obrazek przedstawiający hierarchię
interfejsów:
Interfejs
Collection, oprócz tego że stoi na szczycie hierarchii jest przy okazji dostawcą większości metod, z których korzystają
dziedziczące interfejsy. Do metod tych należą między innymi:
add (dodawanie do kolekcji),
remove (usuwanie z kolekcji),
clear (czyszczenie kolekcji),
size (zwracanie rozmiaru kolekcji).
Zobaczmy teraz czym charakteryzują się poszczególne interfejsy.
- List
Najczęściej używany rodzaj kolekcji. Charakteryzuje się tym, że jej elementy posiadają określoną kolejność. Dodatkowo są one indeksowane,
dzięki czemu można pobrać obiekt znajdujący się na konkretnej pozycji (indeksie) w liście. Na przykład możemy użyć metody get(2),
gdzie 2 to numer pozycji, którą chcemy pobrać. Dostaniemy wtedy trzeci element listy (pozycje są numerowane od 0). Interfejs jest implementowany przez następujące klasy:
- ArrayList
- LinkedList
- Vector (ta klasa jest jeszcze rozszerzana przez Stack, który nie implementuje bezpośrednio interfejsu List)
- Queue
Najważniejszą cechą tej kolejki jest zachowanie kolejności według zasady FIFO (First In - First Out). Oznacza to, że pierwszy element
wprowadzony do kolejki będzie z niej również pobierany jako pierwszy. Interfejs jest implementowany przez:
- Deque (Double ended queue)
Kolejki te, zgodnie z nazwą, wspierają zarówno FIFO jak i LIFO (Last In - First Out). To drugie oznacza, że ostatni element wprowadzony
do kolejki będzie z niej pobierany jako pierwszy. W tym miejscu warto sobie wyobrazić stos, na którym układamy kolejne elementy, by później
zdejmować je począwszy od tego na samej górze i potem dalej w dół. Interfejs jest implementowany przez:
- ArrayDeque
- LinkedList (ta klasa implementuje zarówno interfejs List jak i Deque)
- Set
Najważniejszą cechą tego rodzaju kolekcji jest unikalność jej elementów.
Porówananie obiektów następuje przez wykorzystanie metody equals.
W zbiorze nigdy nie będą istniały dwa obiekty (ani więcej), dla których metoda ta zwraca wartość true.
Sety nie posiadają numerowanych pozycji w postaci indeksów. Zatem nie odwołamy się tutaj do konkretnej pozycji celem pobrania obiektu.
Interfejs jest implementowany przez klasy:
- HashSet
- LinkedHashSet (rozszerza klasę HashSet, ale implementuje też bezpośrednio interfejs Set)
- SortedSet
Zbiory posortowane mają dokładnie te same cechy co "zwykłe" zbiory, z tym że dodatkowo zapewniają one kolejność elementów.
Sortowanie odbywa się na podstawie wykorzystania specjalnej metody, o której jeszcze nie mówiliśmy, ale która pojawi się w niedługim czasie
w naszym kursie. Interfejs jest implementowany przez:
Appa Notka.
Na pierwszy rzut oka liczba możliwości może trochę zniechęcać. Na pocieszenie możemy powiedzieć, że na początku wystarczy opanować klasy ArrayList i
HashSet. Są to najczęściej wykorzystywane klasy w projektach. Oczywiście z upływem czasu należy kontynuować naukę kolejnych klas.
Szukasz dobrego kursu nowej Javy? Mamy dla Ciebie kurs oparty na 150 przykładach.
Kurs nowej Javy składa się z kursu Javy 8 oraz Javy od wersji 9 do 17.
Iterable
W tym miejscu przyszedł czas na małą dygresję związaną ze strukturą przedstawioną powyżej.
Okazuje się, że interfejs
Collection nie jest całkowicie niezależnym bytem. Dziedziczy on z jeszcze innego
interfejsu o nazwie
Iterable. Dzięki temu wszystkie wymienione kolekcje mogą być przeglądane pozycja po pozycji w ramach pętli.
Mówimy wtedy, że iterujemy po kolejnych elementach kolekcji, a to pozwala nam na wykonywanie operacji na kolejnych elementach.
Frameworki (na przykład Spring) często operują w swoich metodach na parametrach implementujących interfejs
Iterable.
Warto to zapamiętać!
Kolekcje par
Na początek mała uwaga. Kolekcje, o których mówiliśmy do tej pory faktycznie wywodzą się z jednego wspólnego interfejsu
Collection.
Te, o których będziemy mówić teraz nie posiadają tej cechy i patrząc tylko z tego punktu widzenia można stwierdzić, że kolekcjami nie są.
Jednakże ich cechy charakterystyczne, takie jak to, że stanowią grupę obiektów oraz to, że możemy przechodzić po obiektach tej grupy w sposób iteracyjny (choć nie wywodzą się z interfejsu
Iterable)
powoduje, że zwyczajowo je również nazywamy kolekcjami. Jest to zgadanienie poruszane dosyć często podczas rozmów kwalifikacyjnych.
Przyjrzyjmy się czym charakteryzują się poszczególne interfejsy.
- Map
Mapy przechowują kolekcję par, a dokładnie pary klucz-wartość. Każdy klucz w mapie jest unikalny, a wartości są przypisane tym kluczom.
Wartości nie są unikalne. Mogą być przypisane do wielu kluczy. Jeśli korzystamy z implementacji interfejsu Map,
to nie mamy zapewnionej kolejności elementów. Interfejs jest implementowany przez klasę:
- HashMap (ta klasa jest jeszcze rozszerzana przez LinkedHashMap )
- SortedMap
Interfejs posiada wszystkie przedstawione własności co Map plus dodatkowo zapewnia odpowiedni porządek sortowania.
Jest on implementowany przez klasę:
Appa Notka.
Mapy są bardzo często wykorzystywane w Javie. Najpopularniejszą z nich jest klasa HashMap i to od niej warto rozpocząć naukę.
Po zapoznaniu się z "haszmapą" z łatwością zrozumiesz pojęcie TreeMap.
Na koniec, dodajmy jeszcze, że
Map nie rozszerza interfejsu
Iterable. Zawiera za to
metody, które umożliwiają operowanie na interfejsach
Set (dla kluczy) oraz
Collection (dla wartości),
ale o tym będziemy już mówić w jednym z kolejnych rozdziałów.
Linki
https://www.geeksforgeeks.org/collections-in-java-2
Masz pytanie dotyczące prezentowanego materiału?
Coś jest dla Ciebie niejasne i Twoje wątpliwości przeszkadzają Ci w pełnym zrozumieniu treści?
Napisz do nas maila, a my chętnie znajdziemy odpowiednie rozwiązanie.
Najciekawsze pytania wraz z odpowiedziami będziemy publikować pod rozdziałem.
Nie czekaj. Naucz się programować jeszcze lepiej.