Znamy już struktury tablicowe i wiemy jakie niedogodności za sobą przynoszą. Z uwagi na ich prostotę często z nich korzystamy i korzystać będziemy, jednakże z uwagi na ich wady nieraz to zastanawiać się możemy czy nie ma czegoś „lepszego”.
Tym „lepszym” mogą się okazać kolekcje, czyli elementy umieszczane w pamięci operacyjnej komputera i powiązane ze sobą specyficznymi, dla natury tej użytej akurat kolekcji, odsyłaczami. Grupa kolekcji to tak naprawdę hierarchia kilku typów interfejsów:

W pakiecie java.util znajduje się interfejs Collection. Tak naprawdę ten interfejs prócz zebrania w sobie nazw metod nie zapewnia ich interpretacji. 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). Dopiero wywodzące się z niego kolejne interfejsy są podstawą do tworzenia struktur charakteryzujących się unikalnymi dlań cechami. Przyjrzyjmy się zatem im bliżej.
Spis treści
Interface List
Ten interfejs może być uznawany za najczęściej używany. Listę charakteryzuje określona kolejność elementów w niej się znajdujących. Odwołujemy się do tych elementów poprzez ich indeks, tak więc np. pobieranie 3 elementu listy będzie wymagało metody .get(2); 1listy podobnie jak tablice numerowane są od 0 Implementacja klas wywodzących z interfejsu List przedstawiona jest poniżej.
Do tego interfejsu należą klasy:
- ArrayList; (implementacja)
- LinkedList; (implementacja)
- Vector (przy okazji – rozszerzana przez Stack nie implementujący bezpośrednio List)
Queue
Najważniejszą cechą tej kolejki jest zachowanie kolejności według zasady FIFO (First In – First Out) – czyli rejestr przesuwny. 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 (tak jak w przypadku stosu). 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ównanie 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. Interfejs jest implementowany przez:
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 zagadnienie 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 SortedMap posiada wszystkie przedstawione własności co Map plus dodatkowo zapewnia odpowiedni porządek sortowania. Jest on implementowany przez klasę:
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ć kiedy indziej.
Zadania do samodzielnego wykonania: Zapoznaliśmy się z wprowadzeniem do kolekcji. Jednakże jest to jedynie czubek góry lodowej, dlatego też spróbuj przerobić temat tworząc na podstawie dokumentacji przykład programu z wykorzystaniem każdej z wymienionych struktur. Zastanów się nad wykorzystaniem tych struktur – w jakich zagadnieniach ta właśnie struktura mogłaby być najwłaściwsza.
Implementacje List
Listy zawierając trzy charakterystyczne klasy są dedykowane dla różnych zachowań. Tak też klasa ArrayList jest dedykowana dla struktur „tablicowych” gdzie optymalizowany jest czas i metoda dostępu do każdego elementu struktury.
ArrayList
Implementacją klasy ArrayList może być poniższy program. Tworzy on dynamicznie rozszerzaną strukturę w pamięci operacyjnej komputera. Nie posiada on ograniczeń tablic, ale potrafi operować indeksami poszczególnych elementów, oraz z uwagi na należenie do interfejsu Iterable, potrafi być w łatwy sposób wyświetlony.
import java.util.List;
import java.util.ArrayList;
import java.util.Scanner;
public class Listy {
public static void main(String[] args) {
Scanner klawiatura=new Scanner(System.in);
String linia;
List<String> lista=new ArrayList();
do {
linia=klawiatura.nextLine();
if (!linia.equals(".")) lista.add(linia); else break;
} while (1==1);
lista.forEach((s) -> {
System.out.println(s);
});
// odpowiednik: for(String s:lista) System.out.println(s);
}
}
Pamiętacie program „Papuga”?! Po jego uruchomieniu możemy stworzyć strukturę zawierającą wszystkie linie wpisane z klawiatury aż do wystąpienia pojedynczej „.” w linii.
Skutkiem działania tego programu będzie coś takiego:

LinkedList
Podobnie działający program możemy szybko napisać dla LinkedList. Różnica jest minimalna:
import java.util.List;
import java.util.LinkedList; //<-- teraz ta biblioteka jest potrzebna.
import java.util.Scanner;
public class Listy {
public static void main(String[] args) {
Scanner klawiatura=new Scanner(System.in);
String linia;
List<String> lista=new LinkedList(); //róznica jest w rodzaju typu - nie ArrayList, a LinkedList... i to wszystko...
do {
linia=klawiatura.nextLine();
if (!linia.equals(".")) lista.add(linia); else break;
} while (1==1);
lista.forEach((s) -> {
System.out.println(s);
});
// odpowiednik: for(String s:lista) System.out.println(s);
}
}
Program działa także w zasadzie identycznie, co widać poniżej. To nie składnia, a zasada działania ma tutaj wpływ na to kiedy użyjemy ArrayList, a kiedy LinkedList.

ArrayList jest optymalizowana do zastosowań związanych z potrzebą dynamicznie zmieniających się rozmiarów tablicy i intensywnych operacji na indeksach takiej „tablicy”. LinkedList zaś dedykowany jest do zastosowań, gdzie niezbyt często poruszamy się po indeksach (jak w tablicy), a znacznie częściej po prostu dodajemy kolejne elementy np. na jej końcu (tworzymy swoisty łańcuch).
Vector
Kilka szybkich zmian kosmetycznych i mamy:
import java.util.List;
import java.util.Scanner;
import java.util.Vector;
public class Listy {
public static void main(String[] args) {
Scanner klawiatura=new Scanner(System.in);
String linia;
List<String> lista=new Vector();
do {
linia=klawiatura.nextLine();
if (!linia.equals(".")) lista.add(linia); else break;
} while (1==1);
lista.forEach((s) -> {
System.out.println(s);
});
// odpowiednik: for(String s:lista) System.out.println(s);
}
}
A wynik … podobny:

Zadanie do samodzielnej pracy studenta: Patrząc na niniejszy temat wróć do zadania z tematu 9: Obsługa plików tekstowych i spróbuj ponownie przyjrzeć się zadaniu specjalnemu – czy potrafisz teraz lepiej zaplanować jego realizację??
