Kolejki, stosy i Deque w praktyce
- Kolekcje i strumienie
- poziom: średni
- aktualizacja: 16 sierpnia 2026
Do stosu i kolejki służy w Javie jedna klasa: ArrayDeque. Ma push, pop i peek jak stos, ma też offer i poll jak kolejka FIFO, a w obu rolach jest szybsza od LinkedList, bo trzyma elementy w tablicy zamiast w łańcuchu obiektów. Stara klasa Stack nadal działa, ale dziedziczy po Vector synchronizację każdej metody, za którą płacisz nawet w jednym wątku — a wydruk demo pokazuje przy okazji drugi powód, żeby jej nie wybierać. Ten skrypt składa z tych klocków trzy typowe zadania: historię cofania z limitem kroków, kolejkę zadań do przetworzenia i wybór trzech najdroższych pozycji cennika. Po drodze celowo wywołuje metody tak, żeby zawiodły: remove() na pustej kolejce, add() na pełnej i offer(null) na ArrayDeque — bo to właśnie te trzy zachowania najczęściej zaskakują na produkcji.
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Stack;
import java.util.concurrent.ArrayBlockingQueue;
/**
* Kolejki, stosy i Deque w praktyce — skryptyJAVA.pl
* Zapisz jako: KolejkiIStosy.java
*/
public class KolejkiIStosy {
/** Pozycja cennika; cena w groszach, żeby nie liczyć pieniędzy na double. */
public record Pozycja(String nazwa, int cenaGrosze) {
@Override
public String toString() {
return String.format("%s %d,%02d zł", nazwa, cenaGrosze / 100, cenaGrosze % 100);
}
}
/** Dokłada stan na wierzchołek historii; najstarszy wypada z przeciwnego końca. */
public static void zapamietaj(Deque<String> historia, String stan, int limit) {
historia.push(stan);
while (historia.size() > limit) {
historia.removeLast();
}
}
/** Zdejmuje ostatni stan; przy pustej historii zwraca null zamiast rzucać wyjątkiem. */
public static String cofnij(Deque<String> historia) {
return historia.poll();
}
/** Przetwarza zadania w kolejności zgłoszenia i zwraca liczbę obsłużonych. */
public static int przetworzZadania(Queue<String> zadania) {
int obsluzone = 0;
// Warunek „aż do null” niesie założenie: kolejka nie wpuszcza null-i. LinkedList je łamie.
for (String zadanie = zadania.poll(); zadanie != null; zadanie = zadania.poll()) {
System.out.println(" wykonuję: " + zadanie + " (zostało " + zadania.size() + ")");
obsluzone++;
}
return obsluzone;
}
/** Kolejka priorytetowa ustawiająca na czele najdroższą pozycję. */
public static PriorityQueue<Pozycja> wedlugCenyMalejaco(List<Pozycja> pozycje) {
Comparator<Pozycja> odNajdrozszych = Comparator.comparingInt(Pozycja::cenaGrosze).reversed();
PriorityQueue<Pozycja> kolejka = new PriorityQueue<>(odNajdrozszych);
kolejka.addAll(pozycje);
return kolejka;
}
/** Zdejmuje kolejne elementy kolejki — priorytet respektuje wyłącznie poll(). */
public static List<Pozycja> zdejmij(Queue<Pozycja> kolejka, int ile) {
List<Pozycja> wynik = new ArrayList<>();
for (int i = 0; i < ile && !kolejka.isEmpty(); i++) {
wynik.add(kolejka.poll());
}
return wynik;
}
// ── PRZYKŁADY ───────────────────────────────────────────
private static void demoStosu() {
System.out.println("== Stos: historia cofania, limit 3 kroki ==");
Deque<String> historia = new ArrayDeque<>();
List.of("pusty dokument", "tytuł", "akapit", "tabela")
.forEach(stan -> zapamietaj(historia, stan, 3));
Stack<String> staryStos = new Stack<>();
historia.descendingIterator().forEachRemaining(staryStos::push);
System.out.println("ArrayDeque wypisuje od wierzchołka: " + historia);
System.out.println("Stack ten sam stos, ale od dna: " + staryStos);
System.out.println("Cofam " + cofnij(historia) + ", na wierzchu zostaje " + historia.peek());
}
private static void demoKolejki() {
System.out.println(System.lineSeparator() + "== Kolejka zadań i dwie rodziny metod ==");
Queue<String> zadania = new ArrayDeque<>(List.of("wyślij fakturę", "przelicz raport"));
zadania.offer("wyczyść cache");
System.out.println("Na czele: " + zadania.peek() + ", czeka zadań: " + zadania.size());
System.out.println("Obsłużono zadań: " + przetworzZadania(zadania));
System.out.println("Pusta: poll() = " + zadania.poll() + ", peek() = " + zadania.peek());
try {
zadania.remove();
} catch (NoSuchElementException e) {
System.out.println("Pusta: remove() rzuca " + e.getClass().getSimpleName());
}
// Na ArrayDeque add i offer robią to samo — różnicę widać dopiero na kolejce z limitem.
Queue<String> ciasna = new ArrayBlockingQueue<>(1);
ciasna.offer("pierwsze");
System.out.println("Pełna: offer() = " + ciasna.offer("drugie"));
try {
ciasna.add("drugie");
} catch (IllegalStateException e) {
System.out.println("Pełna: add() rzuca " + e.getClass().getSimpleName());
}
try {
new ArrayDeque<String>().offer(null);
} catch (NullPointerException e) {
System.out.println("ArrayDeque.offer(null) rzuca " + e.getClass().getSimpleName());
}
// LinkedList null przyjmie — i właśnie tym psuje pętlę „pobieraj, aż wyjdzie null”.
Deque<String> zNullem = new LinkedList<>(List.of("wyślij fakturę"));
zNullem.offerFirst(null);
System.out.println("LinkedList z null-em: obsłużono " + przetworzZadania(zNullem)
+ " zadań, a w kolejce nadal czeka: " + zNullem.peek());
}
private static void demoPriorytetow() {
System.out.println(System.lineSeparator() + "== PriorityQueue: najdroższe pozycje ==");
List<Pozycja> cennik = List.of(new Pozycja("Kawa", 8990), new Pozycja("Filtry", 1250),
new Pozycja("Termometr", 12900), new Pozycja("Ekspres", 249900),
new Pozycja("Waga", 32900), new Pozycja("Młynek", 45900));
PriorityQueue<Pozycja> wgCeny = wedlugCenyMalejaco(cennik);
System.out.println("Surowy toString: " + wgCeny);
System.out.println("Kolejne poll(): " + zdejmij(wgCeny, cennik.size()));
System.out.println("Trzy najdroższe: " + zdejmij(wedlugCenyMalejaco(cennik), 3));
}
public static void main(String[] args) {
demoStosu();
demoKolejki();
demoPriorytetow();
}
}
Jak użyć tego skryptu
- Wklej kod do pliku nazwanego
KolejkiIStosy.java— tak samo brzmi tu klasa publiczna — i odpal go poleceniemjava KolejkiIStosy.java. Wydruk dzieli się na trzy części: stos z historią cofania, kolejkę zadań wraz z reakcją na błędne wywołania oraz kolejkę priorytetową. - Na stos bierz
Deque<String> stos = new ArrayDeque<>()i trzymaj się trójkipush,pop,peek. Nie mieszaj jej zadd: naDequeadddokłada na koniec, apushna początek — po takiej pomyłce kod nadal się kompiluje, tylko stos zachowuje się jak kolejka. - Kolejkę zadań deklaruj jako
Queue<String>, wkładaj przezoffer, pobieraj przezpoll. MetodęprzetworzZadaniamożesz przenieść do siebie bez zmian, ale tylko dla kolejki, która nie wpuszczanull-i — jej pętla kończy pracę na pierwszymnullzpoll, więc karmionaLinkedListpo cichu zostawi resztę zadań (ostatnia linijka drugiej sekcji wydruku). - Po kilka największych elementów sięgaj przez
wedlugCenyMalejacoizdejmij; żeby zmienić kryterium, podmień sam komparator, na przykład naComparator.comparing(Pozycja::nazwa). Kolejka priorytetowa opłaca się wtedy, gdy z dużego zbioru bierzesz tylko czubek — jeśli potrzebujesz całej listy w porządku, czytelniejsze i tak samo szybkie będzielista.stream().sorted(komparator).toList().
Warto wiedzieć
- Iterator
java.util.Stackidzie od dna stosu, a nie od wierzchołka — dotyczy to także pętlifor-each,toStringi strumienia. Kod, który „przegląda historię od najnowszego wpisu”, po cichu pokaże najstarsze. W wydruku widać oba porządki obok siebie: te same trzy stany,ArrayDequeod wierzchołka,Stackod dna. Nie ma na to przełącznika: jeśli musisz zostać przyStack, przejdź go ręcznie przezlistIterator(stos.size())i cofaj się pętląwhile (it.hasPrevious()). - Kolejka ma dwie rodziny metod:
add,removeielementrzucają wyjątek, aoffer,pollipeekzwracająfalsealbonull. NaArrayDeque, która nie ma limitu,addiofferzachowują się przy wstawianiu identycznie — dlatego demo sięga poArrayBlockingQueueo pojemności 1, żeby pokazaćoffer() = falseobokIllegalStateExceptionzadd(). Różnica ujawnia się dopiero, gdy podmienisz implementację na kolejkę z limitem. PriorityQueuegwarantuje porządek wyłącznie na czole kolejki.toString, iterator,forEachistreampokazują surową tablicę kopca — w wydruku najtańsze „Filtry 12,50 zł” stoją na czwartym miejscu, przed droższym „Termometrem”. Jedyny wiarygodny porządek daje ciąg wywołańpoll, dlatego robi to metodazdejmij. Pamiętaj tylko, żezdejmijopróżnia przekazaną kolejkę: po wypisaniu wszystkich pozycjiwgCenyjest już puste, więc demo buduje kopiec drugi raz.ArrayDeque,PriorityQueuei kolejki blokujące odrzucająnullwyjątkiemNullPointerException, aLinkedListgo przyjmuje. To nie jest drobiazg. W kolejce znullwynikpoll() == nullprzestaje znaczyć „kolejka pusta”, więc pętla zprzetworzZadaniakończy się natychmiast — w ostatniej linijce drugiej sekcji raportuje0obsłużonych zadań, choć „wyślij fakturę” dalej stoi w kolejce.LinkedListw roli kolejki wybieraj więc tylko wtedy, gdy naprawdę potrzebujesznull-i albo usuwania ze środka przezListIterator; w pozostałych przypadkachArrayDequejest szybsza i zużywa mniej pamięci, bo nie opakowuje każdego elementu w osobny węzeł.
Podobne skrypty
Stream API w praktyce: filtrowanie, grupowanie i podsumowania
Dziewięć gotowych sekcji Stream API na liście pracowników: filter i map, sortowanie z thenComparing, groupingBy z counting i summingDouble, statystyki, Optional, joining i partitioningBy.
średni Zobacz kod → Kolekcje i strumienieNajczęstsze operacje na List, Map i Set
Operacje, które robisz na kolekcjach codziennie: dodawanie i usuwanie elementów listy, kasowanie duplikatów, część wspólna i różnica zbiorów oraz zliczanie i grupowanie danych w mapie.
początkujący Zobacz kod → Kolekcje i strumienieSortowanie obiektów przez Comparator i Collator
Sortowanie rekordów po wielu polach naraz: rosnąco i malejąco, z bezpieczną obsługą wartości null i polskim porządkiem liter — Comparator, thenComparing i Collator w praktyce.
średni Zobacz kod →