Queue.poll: примеры (JAVA)
Queue.poll: EОписание метода Queue.poll
В Java метод poll() принадлежит интерфейсу java.util.Queue. Он извлекает и возвращает головной элемент очереди, или возвращает null, если очередь пуста. Метод не выбрасывает исключение при пустой очереди, что отличает его от remove().
Подписи и варианты:
E poll()- стандартный метод интерфейсаQueue. Возвращает головной элемент и удаляет его из очереди; при пустой очереди возвращаетnull.E poll(long timeout, java.util.concurrent.TimeUnit unit) throws InterruptedException- перегруженный метод в интерфейсеjava.util.concurrent.BlockingQueue. Пытается извлечь элемент в течение заданного времени, возвращаетnull, если таймаут истёк и элемент не получен; может прерываться и броситьInterruptedException.- В интерфейсе
Dequeдоступны вариантыpollFirst()иpollLast()для операций с обоих концов двунаправленной очереди.
Возвращаемые значения и поведение:
- Возвращает элемент типа
E- голову очереди, после чего этот элемент удаляется. - При пустой очереди возвращает
null(в стандартномpoll()). Следует учитывать, чтоnullможет быть значением, отличающимся от «отсутствия элемента» только если реализация позволяет хранитьnull- но большинство реализаций очередей не допускаютnull. - В блокирующем варианте с таймаутом возвращает
nullпри истечении времени ожидания; при прерывании потока бросаетсяInterruptedException.
Когда используется:
- Когда требуется извлечь и удалить элемент из очереди без риска выбрасывания исключения при пустой структуре.
- В многопоточных сценарииях удобно сочетать
poll()с проверками наnullдля немедленной неблокирующей обработки. - В случаях ожидания доступного элемента в течение ограниченного времени применяется
BlockingQueue.poll(timeout, unit).
Короткие примеры использования poll
Пример 1. LinkedList как Queue
import java.util.*;
Queue<String> q = new LinkedList<>();
q.add("a");
q.add("b");
System.out.println(q.poll());
System.out.println(q.poll());
System.out.println(q.poll());
a b null
Пример 2. ArrayDeque
Queue<Integer> q = new ArrayDeque<>();
q.offer(1);
q.offer(2);
System.out.println(q.poll());
System.out.println(q.peek());
1 2
Пример 3. PriorityQueue
Queue<Integer> pq = new PriorityQueue<>>();
pq.add(10);
pq.add(3);
pq.add(7);
System.out.println(pq.poll());
System.out.println(pq.poll());
System.out.println(pq.poll());
3 7 10
Пример 4. BlockingQueue с таймаутом
import java.util.concurrent.*;
BlockingQueue<String> bq = new ArrayBlockingQueue<>(2);
bq.offer("x");
try {
String s = bq.poll(500, TimeUnit.MILLISECONDS);
System.out.println(s);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println("interrupted");
}
x
Похожие методы в Java и их особенности
- remove() - извлекает голову очереди, но при пустой структуре бросает
NoSuchElementException. Предпочтительнее, когда пустая очередь считается ошибкой. - peek() - возвращает голову без удаления, при пустой очереди возвращает
null. Удобен для проверки следующего элемента без изменения очереди. - poll(long, TimeUnit) (в
BlockingQueue) - блокирующий вариант с таймаутом и возможностью прерывания. Подходит для ожидания в многопоточном окружении. - take() (в
BlockingQueue) - блокирует до получения элемента, бросаетInterruptedException. Используется, когда требуется гарантированное получение. - pollFirst()/pollLast() (в
Deque) - извлечение с определённого конца. Применяется при реализации двунаправленной очереди.
Эквиваленты в других языках и отличия
PHP
$q = new SplQueue();
$q->enqueue('a');
$q->enqueue('b');
echo $q->dequeue();
echo PHP_EOL;
echo $q->dequeue();
a b
Особенности: dequeue() бросает RuntimeException при пустой очереди; поведения по умолчанию нет варианта, возвращающего null.
JavaScript
const q = ['a','b'];
console.log(q.shift());
console.log(q.shift());
console.log(q.shift());
a b undefined
Особенности: Array.shift() возвращает undefined на пустом массиве. Операция O(n) для массивов, для производительных очередей рекомендуются двусвязные списки или специализированные структуры.
Python
from collections import deque
q = deque(['a','b'])
print(q.popleft())
print(q.popleft())
# queue.Queue для потокобезопасного варианта
import queue
bq = queue.Queue()
bq.put('x')
print(bq.get_nowait())
a b x
Особенности: deque.popleft() быстрое O(1); queue.Queue.get() может блокировать или бросать исключение при использовании get_nowait().
SQL
-- концепт: выбрать и удалить одну строку в порядке FIFO
BEGIN TRANSACTION;
SELECT id, payload FROM messages ORDER BY created_at LIMIT 1;
DELETE FROM messages WHERE id = ?;
COMMIT;
-- результат зависит от данных в таблице
Особенности: операции требуют транзакций для атомарности; нет встроенного метода poll.
C#
var q = new Queue<string>();
q.Enqueue("a");
q.Enqueue("b");
Console.WriteLine(q.Count > 0 ? q.Dequeue() : null);
// ConcurrentQueue в .NET имеет TryDequeue
var cq = new System.Collections.Concurrent.ConcurrentQueue<string>();
cq.Enqueue("x");
if (cq.TryDequeue(out var v)) Console.WriteLine(v);
a x
Особенности: Dequeue() бросает InvalidOperationException при пустой очереди; в многопоточном варианте доступны TryDequeue.
Go
// использование канала
ch := make(chan int, 2)
ch <- 1
ch <- 2
fmt.Println(<-ch)
// non-blocking receive
select {
case v := <-ch:
fmt.Println(v)
default:
fmt.Println("empty")
}
1 2
Особенности: каналы обеспечивают блокирующее и неблокирующее чтение через select; для структуры списка можно использовать container/list.
Kotlin
val q: java.util.Queue<String> = java.util.ArrayDeque()
q.add("a")
println(q.poll())
println(q.poll())
a null
Особенности: Kotlin использует те же реализации, что и Java; сигнатуры и поведение аналогичны.
Lua
local q = { 'a', 'b' }
print(table.remove(q, 1))
print(table.remove(q, 1))
print(table.remove(q, 1))
a b nil
Особенности: table.remove с индексом 1 реализует pop-перед, но сдвигает все элементы - менее эффективно для больших очередей.
Типичные ошибки при использовании poll
- Ожидание исключения вместо проверки
null. Многие примеры предполагают, чтоpoll()бросает исключение при пустой очереди, тогда как он возвращаетnull. Пример ошибки:
Queue<String> q = new LinkedList<>();
String s = q.poll();
// Ошибка: если считать, что s нельзя сравнивать с null без проверки
System.out.println(s.length());
Exception in thread "main" java.lang.NullPointerException at ...
- Использование
poll()вместо блокирующего метода в многопоточной логике, где нужно дождаться элемента. Это может приводить к постоянному получениюnullи бессмысленной работе цикла. - Игнорирование
InterruptedExceptionвpoll(timeout,...). Неправильная обработка прерывания приводит к непредсказуемому поведению потоков. - Предположение о порядке элементов в
PriorityQueue. При использовании приоритетной очереди извлекается наименьший элемент по сравнению, а не в порядке вставки. - Попытка хранить
nullв качестве элемента. Большинство реализаций очередей не допускаютnull, и вставка может броситьNullPointerException. - Неправильная синхронизация при использовании несинхронизированных реализаций в многопоточном окружении - лучше применять concurrent-реализации.
Изменения в методе в последних версиях Java
Метод poll() как таковой интерфейса Queue остался стабильным и не подвергался существенным изменениям в последних версиях языка. Значимые дополнения в экосистеме:
- С момента появления пакета
java.util.concurrentпоявились расширения: блокирующий вариантpoll(timeout, unit)вBlockingQueue, а также новые concurrent-реализации очередей. - Ввод default-методов в интерфейсах коллекций не затронул базовое поведение
poll().
Вывод: явных изменений в семантике метода в современных релизах Java нет, но развивались и добавлялись специализированные реализации и методы для конкурентных сценариев.
Расширенные примеры и нестандартные сценарии
1) Дрейн очереди в пакетах для обработки
Queue<String> q = new ConcurrentLinkedQueue<>();
q.add("a");
q.add("b");
q.add("c");
int maxBatch = 2;
List<String> batch = new ArrayList<>();
String item;
while ((item = q.poll()) != null && batch.size() < maxBatch) {
batch.add(item);
}
System.out.println(batch);
System.out.println(q);
[a, b] [c]
Пояснение: позволяет обрабатывать элементы пакетами без блокирования, сохраняя оставшиеся элементы в очереди.
2) Producer-consumer с таймаутом и корректной обработкой прерываний
import java.util.concurrent.*;
BlockingQueue<String> q = new ArrayBlockingQueue<>(10);
// consumer
Runnable consumer = () -> {
try {
while (true) {
String s = q.poll(1, TimeUnit.SECONDS);
if (s == null) {
System.out.println("timed out, shutdown check");
break; // пример остановки при простое
}
System.out.println("consumed " + s);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
};
// producer
q.offer("task1");
new Thread(consumer).start();
consumed task1 timed out, shutdown check
Пояснение: poll с таймаутом позволяет корректно завершать потребителя при отсутствии задач.
3) Комбинация poll и Optional для компактной обработки
Queue<String> q = new LinkedList<>();
q.add("x");
java.util.Optional.ofNullable(q.poll())
.ifPresent(v -> System.out.println("got: " + v));
got: x
Пояснение: уменьшает количество явных проверок на null.
4) Выполнение фоновых задач с адаптацией интервала по загрузке
Queue<Runnable> q = new ConcurrentLinkedQueue<>();
q.add(() -> System.out.println("job1"));
q.add(() -> System.out.println("job2"));
int idleCount = 0;
while (idleCount < 3) {
Runnable job = q.poll();
if (job == null) {
idleCount++;
try { Thread.sleep(100 * idleCount); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
} else {
idleCount = 0;
job.run();
}
}
System.out.println("stopped after idle");
job1 job2 stopped after idle
Пояснение: адаптивная задержка при простое уменьшает потребление ресурсов, избегая блокирования.
5) Использование pollFirst/pollLast для Deque
Deque<String> d = new ArrayDeque<>();
d.add("a");
d.add("b");
System.out.println(d.pollFirst());
System.out.println(d.pollLast());
System.out.println(d.pollFirst());
a b null
Пояснение: полезно при реализации deque-очередей с приоритетной обработкой концов.
6) Использование poll в неблокирующем цикле для graceful shutdown
AtomicBoolean running = new AtomicBoolean(true);
Queue<String> q = new ConcurrentLinkedQueue<>();
// другой поток добавит null-метку и вызовет running.set(false) для shutdown
while (running.get() || !q.isEmpty()) {
String s = q.poll();
if (s != null) {
System.out.println(s);
}
}
System.out.println("exited loop");
-- вывод зависит от добавляемых данных exited loop
Пояснение: сочетание флага и очистки очереди обеспечивает корректное завершение без блокирования.