Kolekcje Javy – Interfejs List – ArrayList

W tym artykule przyjrzymy się gotowym kolekcjom opartych na interfejsie List. Obie omawiane klasy implementują go oraz interfejs Iterable w związku z tym ich obsługa z puntu widzenia programisty będzie bardzo podobna. Różnice polegają głównie na sposobie reprezentacji danych w pamięci i algorytmów obsługi takich jak dodawanie elementów, odczyt czy też usuwanie. Wpis jest częścią artykułów o kolekcjach w Javie.

Klasa ArrayList

ArrayList jest implementacją listy, w taki sposób, że kolejne elementy znajdują się obok siebie w wewnętrznej tablicy. Element i oraz i+1 sąsiadują ze sobą nie tylko jako elementy kolekcji, ale również w pamięci operacyjnej komputera. Główną różnicą w stosunku do poznanych wcześniej tablic  jest to, że rozmiar może zmieniać się dynamicznie w trakcie działania programu.

W Javie co prawda tablice pozwalają na ustalenie rozmiaru tablicy po uruchomieniu aplikacji, jednak jeśli już utworzymy taką tablicę, to nie można zmieniać jej rozmiarów. W przypadku ArrayList to ograniczenie nie istnieje i można dowolnie rozszerzać lub zmniejszać rozmiar kolekcji.

ArrayList posiada w swojej implementacji pojemność (ang. capacity). Jej rozmiar jest co najmniej tak duży jak rozmiar przechowywanej kolekcji. Nie jest sprecyzowane w jaki sposób i kiedy rozszerza się pojemność kolekcji. Wiadomo tylko, że dodawanie elementów posiada zamortyzowany koszt.

Kolekcje ArrayList nie są automatycznie synchronizowane w związku z czym w przypadku dostępu do kolekcji przez wiele wątków należy zadbać o mechanizm synchronizacji samemu. Jednakże problemem wielowątkowości aplikacji zajmiemy się w przyszłości.

ArrayList – jak używać

Klasa ArrayList jest klasą generyczną co oznacza, że można przechowywać w kolekcji dowolny typ obiektowy. W praktyce istnieje jeszcze stara wersja, która przechowuje obiekty klasy Object, ale w obecnej wersji Javy nie ma sensu korzystać z niej gdyż korzystanie z niej wymaga niewygodnego rzutowania, którego zalecamy unikać gdy nie jest konieczne.

Konstruktory oraz tworzenie obiektu listy

  • ArrayList<E>() – tworzy pustą listę o pojemności 10.
  • ArrayList<E>(int capacity) – Tworzy pustą listę o pojemności podanej w parametrze.

Aby utworzyć pustą listę tablicową można użyć następującego kodu.

Można też skorzystać ze zmiennej interfejsu List.

Metody dostarczone przez klasę ArrayList

  • boolean add(E element) – Dodaje na końcu listy element.
  • void add(int index, E element) – dodaje element na pozycji index (licząc od 0). Element znajdujący się dotychczas na tej pozycji oraz wszystkie o indeksie większym zostają przesunięte w prawo.
  • E remove(int index) – usuwa z listy element o wskazanym indeksie. Pozostałe elementy za nim przesuwa w lewo.
  • void clear() – usuwa wszystkie elementy z listy.
  • E set(int index, E element) – zamienia element we wskazanym indeksie na ten podany w argumencie.
  • <T> T[] toArray(T[] array) – zwraca listę w postaci tablicowej.
  • void trimToSize() – zmniejsza pojemność wewnętrznej tablicy listy do rozmiaru kolekcji.
  • E get(int index) – zwraca element o podanym indeksie.
  • ListIterator<E> listIterator(int index) – zwraca iterator umieszczony na obiekcie o podanym indeksie.
  • void sort(Comparator<? super E> collection) – sortuje listę wg. podanego komparatora. Aby było to możliwe musimy zaimplementować interfejs Comparable oraz metodę compareTo w obiektach klas, które są elementami kolekcji.
  • int size()– zwraca rozmiar listy (liczbę aktualnie przechowywanych obiektów, a nie pojemności listy).

Poniżej pokażemy jak w praktyce używać podanych wyżej metod.

ArrayList podsumowanie

Na potrzeby akademickie ta klasa jest w zupełności wystarczająca gdyż zapewnia wszystkie dostępne operacje potrzebne do obsługi list i jest dobrze zoptymalizowana. Największe zastrzeżenie może budzić sytuacja, gdy należy modyfikować dużo elementów w środku kolekcji. Wtedy ArrayList wykonuje różne czynności, które nie są zbyt efektywne. Może się okazać, że korzystniejsze będzie użycie drugiej implementacji czyli list powiązanych LinkedList dlatego polecamy używania zmiennej interfejsu List gdyby zaszła potrzeba zmiany jednej implementacji na drugą.