Queue.peek: примеры (JAVA)
Queue.peek: EОбщее описание метода
Метод Queue.peek() из пакета java.util возвращает элемент в голове очереди, не удаляя его. Применяется для получения текущего «первого» элемента без изменения структуры очереди. Возвращаемый тип - обобщённый E, соответствующий параметру типа очереди.
Сигнатура: E peek(). Аргументов нет. Поведение:
- Если очередь не пуста, возвращается элемент, находящийся в голове.
- Если очередь пуста, возвращается
null. Это отличает peek() от методаelement(), который при пустой очереди выбрасываетNoSuchElementException.
Особенности в разных реализациях:
- Для
PriorityQueue«голова» - наименьший (или наибольший) элемент в соответствии с компаратором. - Для реализаций из пакета
java.util.concurrentзначение, возвращаемое peek(), может быть устаревшим в многопоточной среде, так как операция не синхронизирована сама по себе.
Ограничения и требования:
- Некоторые реализации не допускают
nullв качестве элемента; попытка вставкиnullприводит к исключению (например,PriorityQueueи большинство concurrent-реализаций). - Сложность выполнения зависит от реализации: для большинства очередь-списков это O(1), для приоритетных - O(1) для чтения головы, но поддержание структуры при вставке/удалении иного характера.
Короткие примеры применения
Пример 1: LinkedList как очередь, чтение головы без удаления.
import java.util.*;
Queue q = new LinkedList<>();
q.add("a");
q.add("b");
String head = q.peek();
System.out.println(head);
System.out.println(q);
a [a, b]
Пример 2: Поведение для пустой очереди.
Queue q = new ArrayDeque<>();
System.out.println(q.peek());
// сравнение с element()
try {
System.out.println(q.element());
} catch (Exception e) {
System.out.println(e.getClass().getSimpleName());
}
null NoSuchElementException
Пример 3: PriorityQueue возвращает элемент по приоритету.
Queue pq = new PriorityQueue<>();
pq.add(20);
pq.add(5);
pq.add(15);
System.out.println(pq.peek());
System.out.println(pq);
5 [5, 20, 15] // внутреннее представление может отличаться
Пример 4: ConcurrentLinkedQueue - неблокирующее чтение головы в многопоточной среде.
import java.util.concurrent.*;
Queue cq = new ConcurrentLinkedQueue<>();
cq.add("x");
System.out.println(cq.peek());
// другой поток может одновременно изменить очередь
x
Похожие методы в Java
Краткий обзор альтернатив и отличий:
element()- возвращает голову, но при пустой очереди выбрасываетNoSuchElementException. Подходит, когда пустая очередь считается ошибкой.poll()- возвращает и удаляет голову; при пустой очереди возвращаетnull. Предпочтительнее для потребления элементов.remove()- удаляет и возвращает голову, при пустой очереди выбрасываетNoSuchElementException. Аналогиченelement()по семантике ошибок.- В интерфейсе
DequeдоступныpeekFirst()иpeekLast()для двусторонних очередей, когда требуется смотреть соответствующие концы. - Для блокирующих очередей (
BlockingQueue) имеются методыtake()(блокирует до появления элемента) иpoll(timeout, unit)(ждёт ограниченное время).
Сопоставление с другими языками
Краткие аналоги и ключевые отличия в популярных языках с примерами.
JavaScript - массив или очередь из библиотек. Псевдоприкладной пример:
const q = ['a','b'];
const head = q[0];
console.log(head);
console.log(q);
a ['a','b']
В JS встроенной семантики очереди нет: доступ по индексу возвращает undefined для пустой коллекции вместо исключения.
Python: коллекция deque и класс queue.Queue.
from collections import deque
q = deque(['a','b'])
print(q[0])
# deque[0] при пустой очереди вызовет IndexError
a
Класс queue.Queue предназначен для потокобезопасного взаимодействия и не предоставляет прямого безопасного peek; обходные пути включают использование блокировок или внутренних очередей (не рекомендуется).
C#: System.Collections.Generic.Queue<T> имеет Peek(), который при пустой очереди бросает InvalidOperationException.
var q = new Queue();
q.Enqueue("a");
Console.WriteLine(q.Peek());
a
Go: стандарт - срезы или кольцевые буферы. «Peek» реализуется как обращение к элементу по индексу, при пустом срезе будет паника.
q := []string{"a","b"}
fmt.Println(q[0])
a
Kotlin: может использовать java.util.Queue и методы peek() а также удобные расширения: firstOrNull() для коллекций.
val q: java.util.Queue = ArrayDeque()
q.add("a")
println(q.peek())
a
PHP: класс SplQueue допускает методы bottom() и top() для доступа к концам; получение без удаления обычно делается через top() после перехода итератора.
$q = new SplQueue();
$q->enqueue('a');
echo $q->top();
a
Общее отличие: в Java peek() возвращает null при пустой очереди, тогда как в ряде языков доступ к первому элементу бросит исключение или вернёт спецзначение (undefined/None).
Типичные ошибки и ловушки
- Ожидание исключения при пустой очереди. Пример ошибки: использование peek() вместо
element()при логике, где пустота - ошибка.
Queue q = new ArrayDeque<>();
if (q.peek() == null) {
// намерение: обработать как ошибка, но тут возвращается null
System.out.println("пусто");
}
// если требовалось исключение, использовался бы element()
пусто
- Попытка положить
nullв очередь и затем читать. Многие реализации (например,PriorityQueueи concurrent-очереди) запрещаютnull, что приводит кNullPointerExceptionпри добавлении.
Queue pq = new PriorityQueue<>();
try {
pq.add(null);
} catch (Exception e) {
System.out.println(e.getClass().getSimpleName());
}
NullPointerException
- Неправильное использование в многопоточности: peek() не атомарен с операциями, зависящими от возвращённого значения. Пример гонки: проверка через
peek(), затем условное удаление черезpoll()без синхронизации приводит к тому, что другой поток может удалить элемент между вызовами.
// возможная гонка
Queue q = new ConcurrentLinkedQueue<>();
String e = q.peek();
if (e != null && e.equals("task")) {
// другой поток мог уже удалить этот элемент
q.remove(e);
}
// поведение зависит от interleaving, возможны пропуски или дублированная обработка
Изменения в реализации за последние версии
Метод peek() как часть интерфейса Queue сохраняет стабильную семантику в современных релизах Java. В последних версиях не происходило изменения его базового контракта: отсутствие аргументов и возвращение null при пустой очереди. Развитие коснулось в основном новых реализаций очередей в пакете java.util.concurrent, которые обеспечивают дополнительные гарантии по многопоточности и эффективности, но не меняют поведение peek().
Расширенные и нетипичные сценарии
Сценарий 1: Планировщик задач с приоритетом по времени. peek() проверяет ближайшую задачу без её удаления; при наступлении времени выполняется poll().
import java.util.*;
class Task implements Comparable {
long time;
String name;
Task(long t, String n){time=t;name=n;}
public int compareTo(Task o){return Long.compare(this.time,o.time);}
public String toString(){return name+"@"+time;}
}
PriorityQueue pq = new PriorityQueue<>();
pq.add(new Task(System.currentTimeMillis()+2000, "t1"));
// имитация цикла планировщика:
Task next = pq.peek();
System.out.println("next="+next);
next=t1@1600000000000 // пример вывода, время будет реальным
Комментарий: использование peek() позволяет принимать решение о времени ожидания до следующей операции, не потребляя задачу преждевременно.
Сценарий 2: Безопасная обработка с Optional, чтобы явно работать с отсутствием значения.
import java.util.*;
Queue q = new ArrayDeque<>();
q.add("job");
Optional.ofNullable(q.peek()).ifPresent(s -> System.out.println("head: "+s));
head: job
Сценарий 3: Проверка состояния очереди в алгоритме с откатом. peek() даёт возможность посмотреть, что будет обработано, а при необходимости элемент можно удалить позже.
Queue q = new LinkedList<>();
q.add(1); q.add(2);
Integer x = q.peek();
if (x != null && x > 0) {
// выполнить проверку, и только после подтверждения удалить
Integer removed = q.poll();
System.out.println("removed="+removed);
}
System.out.println(q);
removed=1 [2]
Сценарий 4: Использование в неблокирующем кешировании: peek() проверяет, истёк ли верхний элемент по таймстампу, перед попыткой извлечения или обновления.
// псевдокод: очередь пар (expiry, value)
Queue q = new LinkedList<>();
q.add(new long[]{System.currentTimeMillis()+1000, 42});
long[] top = q.peek();
if (top != null && top[0] <= System.currentTimeMillis()){
q.poll(); // удалить просроченный
}
// поведение зависит от времени, пример: элемент может быть удалён после истечения срока
Важная ремарка: при использовании в многопоточном окружении требуется продуманная стратегия синхронизации, так как последовательность peek() и последующих операций не атомарна.