Що таке Queue і які реалізації існують?
4. ConcurrentLinkedQueue — lock-free 5. BlockingQueue — Producer-Consumer 6. null заборонений у більшості черг 7. SynchronousQueue — для прямої передачі
🟢 Junior Level
Queue (Черга) — структура даних за принципом FIFO (First In, First Out).
Проста аналогія: Черга в магазині. Хто перший прийшов, той перший обслужений.
Основні методи:
| Дія | Виняток | Безпечний метод |
|---|---|---|
| Додати | add(e) |
offer(e) |
| Забрати | remove() |
poll() |
| Подивитися | element() |
peek() |
Queue<String> queue = new ArrayDeque<>();
queue.offer("A"); // Додали
queue.offer("B");
String first = queue.poll(); // → "A" (перший пішов)
String next = queue.peek(); // → "B" (подивилися)
offer/poll/peek переважніші у production-коді, тому що вони не кидають винятки — повертають false/null при помилці, що дозволяє обробити ситуацію gracefully.
🟡 Middle Level
Основні реалізації
ArrayDeque:
// Кільцевий масив → O(1) вставка/видалення
Queue<String> queue = new ArrayDeque<>();
queue.offer("A"); // Швидко
queue.poll(); // Швидко
PriorityQueue:
// Binary Heap → вилучення за пріоритетом
Queue<Integer> pq = new PriorityQueue<>();
pq.offer(5);
pq.offer(1);
pq.offer(3);
pq.poll(); // → 1 (найменший!)
Методи: Exception vs Special Value
// Кидають exception:
queue.add(e); // IllegalStateException якщо full
queue.remove(); // NoSuchElementException якщо пуста
queue.element(); // NoSuchElementException якщо пуста
// Повертають special value:
queue.offer(e); // false якщо full
queue.poll(); // null якщо пуста
queue.peek(); // null якщо пуста
// ✅ В production використовуйте offer/poll/peek!
Thread-safe черги
// Lock-free (висока інтенсивність)
Queue<String> q = new ConcurrentLinkedQueue<>();
// Blocking (Producer-Consumer)
BlockingQueue<String> q = new LinkedBlockingQueue<>();
q.put("A"); // Блокує якщо full
String e = q.take(); // Блокує якщо empty
// Bounded (контроль пам'яті)
BlockingQueue<String> q = new ArrayBlockingQueue<>(100);
// → Не більше 100 елементів → захист від OOM
🔴 Senior Level
Коли НЕ використовувати Queue
- Якщо потрібен доступ до довільного елемента — візьміть List
- Якщо важлива унікальність — візьміть Set
- Якщо дані потрібно сортувати — PriorityQueue з компаратором або TreeSet
Чому черги не люблять null?
// Більшість черг забороняють null
queue.offer(null); // → NullPointerException!
// Чому?
// poll() та peek() повертають null для порожньої черги
// Якщо null валідний елемент → двозначність!
// → "черга пуста" чи "в голові null"?
BlockingQueue патерни
// Producer-Consumer
BlockingQueue<Task> queue = new LinkedBlockingQueue<>(1000);
// Producer
queue.put(task); // Блокує якщо full → backpressure!
**Backpressure** (зворотний тиск) — механізм, при якому повільний споживач сигналізує швидкому виробнику сповільнитися. Bounded Queue блокує producer, поки consumer не звільнить місце.
// Consumer
Task task = queue.take(); // Блокує якщо empty
SynchronousQueue
// Черга з нульовою ємністю!
BlockingQueue<String> q = new SynchronousQueue<>();
// put блокує, поки хтось не зробить take
// Блокування = потік «засинає» та не витрачає CPU, поки інший потік не забере елемент. Це ефективно для синхронізації без активного очікування (busy-wait).
q.put("A"); // Чекає...
// В іншому потоці:
String s = q.take(); // → "A", put розблоковується
// Використання: Executors.newCachedThreadPool()
DelayQueue
// Елементи доступні тільки після затримки
class DelayedTask implements Delayed {
private final long deadline;
public long getDelay(TimeUnit unit) {
return unit.convert(deadline - System.currentTimeMillis(), MILLISECONDS);
}
}
DelayQueue<DelayedTask> queue = new DelayQueue<>();
// poll() поверне елемент тільки коли getDelay() <= 0
TransferQueue (Java 7+)
// Producer чекає, поки Consumer забере елемент
TransferQueue<String> q = new LinkedTransferQueue<>();
// Producer
q.transfer("message"); // Блокує поки consumer не забере!
// Consumer
String msg = q.take(); // Забирає, producer розблоковується
// Використання: синхронна передача даних
// Синхронна передача = producer чекає конкретного моменту, коли consumer *прийме* елемент (а не просто покладе у буфер). Аналогія: передача файлу з рук в руки, а не залишення у поштовій скриньці.
Production Experience
Реальний сценарій: Bounded Queue врятувала від OOM
- Producer швидший за Consumer → черга росла
- Unbounded Queue → OOM через 2 години
- Рішення: ArrayBlockingQueue(10000)
- Результат: Producer блокується → стабільна система
Best Practices
- offer/poll/peek замість add/remove/element
- ArrayDeque — для single-thread
- Bounded Queue — для backpressure
- ConcurrentLinkedQueue — lock-free
- BlockingQueue — Producer-Consumer
- null заборонений у більшості черг
- SynchronousQueue — для прямої передачі
Резюме для Senior
- Queue = FIFO, offer/poll/peek переважніші
- ArrayDeque — fastest single-thread
- BlockingQueue — Producer-Consumer з backpressure
- Bounded — захист від OOM
- ConcurrentLinkedQueue — lock-free
- TransferQueue — синхронна передача
- null заборонений (двозначність з poll)
🎯 Шпаргалка для інтерв’ю
Обов’язково знати:
- Queue = FIFO (First In, First Out), методи: offer/poll/peek переважніші за add/remove/element
- offer/poll/peek повертають false/null замість exception — безпечніше для production
- ArrayDeque — fastest single-thread (кільцевий масив, O(1) вставка/видалення)
- PriorityQueue = Binary Heap, вилучення за пріоритетом (найменший елемент), O(log n)
- BlockingQueue = Producer-Consumer патерн, put/take блокують, bounded queue = backpressure
- SynchronousQueue = нульова ємність, producer чекає поки consumer забере елемент
- TransferQueue (Java 7+) — синхронна передача: transfer() блокує поки consumer не забере
- null заборонений у більшості черг — двозначність з poll() що повертає null
Часті уточнюючі запитання:
- Чому offer/poll/peek кращі за add/remove/element? — Не кидають exception, дозволяють обробити gracefully
- Навіщо bounded queue? — Backpressure: повільний споживач блокує швидкого виробника, захист від OOM
- Чим SynchronousQueue відрізняється від звичайної черги? — Нульова ємність, put блокує поки хтось не зробить take
- Коли використовувати TransferQueue? — Для синхронної передачі даних, producer чекає конкретного моменту прийому
Червоні прапорці (НЕ говорити):
- ❌ “Queue дозволяє зберігати null” — більшість реалізацій забороняють null (двозначність з poll)
- ❌ “Unbounded Queue — нормальний вибір для production” — без bounded queue ризик OOM від швидкого виробника
- ❌ “add/remove/element — найкращий вибір” — вони кидають exception, offer/poll/peek безпечніше
- ❌ “PriorityQueue вилучає найбільший елемент” — за замовчуванням вилучає НАЙМЕНШИЙ (потрібен зворотний Comparator для найбільшого)
Пов’язані теми:
- [[Які основні інтерфейси Collection Framework]]
- [[Що таке Deque]]
- [[В чому різниця між List, Set та Queue]]
- [[Що таке Stack]]