LinkedList to jedna z fundamentalnych struktur danych oferowanych przez język Java w ramach kolekcji w bibliotece standardowej. Jest to implementacja interfejsu List, która pozwala na efektywne zarządzanie elementami w kolekcji dzięki swojej dynamicznej strukturze. Pozwala na przechowywanie danych w uporządkowanej formie z zachowaniem kolejności wstawiania elementów, a jednocześnie umożliwia efektywne operacje na początku i końcu listy.
Struktura LinkedList
LinkedList jest oparta na węzłach, z których każdy przechowuje dane i referencje do poprzedniego oraz następnego węzła. Ta struktura nazywana jest listą dwukierunkową (ang. doubly linked list). Dzięki temu:
- Wstawianie elementów na początku lub na końcu listy jest bardzo szybkie – odbywa się w czasie O(1).
- Operacje na elementach w środku listy, takie jak dodawanie czy usuwanie, mogą wymagać przejścia przez kolejne węzły, co zajmuje czas O(n).
Każdy węzeł w LinkedList składa się z trzech głównych części:
- Referencji do poprzedniego węzła – umożliwia cofanie się w strukturze.
- Danych – właściwy element przechowywany w liście.
- Referencji do następnego węzła – pozwala na przechodzenie w przód.
Tworzenie LinkedList
Przykładowa inicjalizacja LinkedList w Javie wygląda następująco:
import java.util.LinkedList;
public class Main {
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>();
// Dodawanie elementów do listy
list.add("Element 1");
list.add("Element 2");
list.add("Element 3");
// Wyświetlenie zawartości listy
System.out.println("Lista: " + list);
}
}W powyższym przykładzie utworzono listę przechowującą obiekty typu String. Metoda add() dodaje element na końcu listy.
Główne metody w LinkedList
LinkedList dostarcza wielu przydatnych metod, które pozwalają na manipulację jej zawartością:
- Dodawanie elementów:
add(E e)– dodaje element na końcu listy.add(int index, E element)– wstawia element na określonym indeksie.addFirst(E e)– dodaje element na początku listy.addLast(E e)– dodaje element na końcu listy (tożsama zadd(E e)).
- Usuwanie elementów:
remove()– usuwa pierwszy element listy.remove(int index)– usuwa element na wskazanym indeksie.removeFirst()– usuwa pierwszy element listy.removeLast()– usuwa ostatni element listy.
- Pobieranie elementów:
get(int index)– zwraca element na wskazanym indeksie.getFirst()– zwraca pierwszy element listy.getLast()– zwraca ostatni element listy.
- Sprawdzanie zawartości:
contains(Object o)– sprawdza, czy lista zawiera dany element.isEmpty()– zwracatrue, jeśli lista jest pusta.
- Inne:
size()– zwraca liczbę elementów w liście.clear()– usuwa wszystkie elementy z listy.
Przykłady zastosowań
Kolejka
LinkedList może być używana jako implementacja kolejki dzięki metodom addFirst i removeLast:
LinkedList<String> queue = new LinkedList<>();
queue.addLast("Zadanie 1");
queue.addLast("Zadanie 2");
queue.addLast("Zadanie 3");
System.out.println("Pierwsze zadanie: " + queue.removeFirst());Stos
Można również wykorzystać LinkedList jako stos (Last-In-First-Out, LIFO):
LinkedList<String> stack = new LinkedList<>();
stack.addFirst("Element 1");
stack.addFirst("Element 2");
stack.addFirst("Element 3");
System.out.println("Zdejmowany element: " + stack.removeFirst());Przechowywanie historii przeglądarki
LinkedList świetnie nadaje się do implementacji historii przeglądania stron internetowych, gdzie użytkownik może przechodzić wstecz i do przodu:
LinkedList<String> history = new LinkedList<>();
history.add("Strona główna");
history.add("Strona 1");
history.add("Strona 2");
System.out.println("Aktualna strona: " + history.getLast());
history.removeLast();
System.out.println("Powrót do: " + history.getLast());Wydajność LinkedList
Zrozumienie wydajności operacji w LinkedList jest kluczowe do jej efektywnego wykorzystania:
- Dodawanie na początku/końcu: O(1) – dzięki wsparciu referencji do pierwszego i ostatniego węzła.
- Dostęp do elementu: O(n) – konieczne jest przejście przez węzły od początku lub końca listy.
- Usuwanie elementów: O(1) – na początku lub końcu; O(n) – w środku.
W związku z tym, LinkedList sprawdza się w scenariuszach, gdzie operacje są wykonywane głównie na końcu lub początku listy.
LinkedList vs ArrayList
Porównanie tych dwóch popularnych implementacji interfejsu List jest często zadawanym pytaniem:
| Cecha | LinkedList | ArrayList |
|---|---|---|
| Struktura danych | Lista dwukierunkowa | Dynamiczna tablica |
| Wydajność dostępu | O(n) | O(1) |
| Wstawianie/usuwanie | O(1) na początku/końcu | O(n) |
| Zużycie pamięci | Większe | Mniejsze |
Praktyczne wskazówki
- Korzystaj z LinkedList, gdy konieczne są częste operacje na początku lub końcu listy.
- Unikaj LinkedList, jeśli dostęp do elementów w środku listy jest kluczowy – lepiej wtedy wybrać ArrayList.
- Pamiętaj, że LinkedList nie jest synchronizowana, co oznacza, że w środowiskach wielowątkowych należy dodatkowo zadbać o bezpieczeństwo wątków (np. używając
Collections.synchronizedList()).
LinkedList to potężne narzędzie w rękach programisty, szczególnie w sytuacjach, gdy wymagane są dynamiczne zmiany w strukturze danych. Dzięki swoim unikalnym cechom może być podstawą wielu aplikacji i systemów, gdzie optymalizacja operacji na danych jest kluczowa.