Deque.pollLast: примеры (JAVA)

Использование pollLast() для извлечения элемента с конца
Раздел: Коллекции (Collection Framework) - Queue/Deque
Deque.pollLast: E

Общее описание метода pollLast()

Метод pollLast() интерфейса java.util.Deque извлекает и удаляет последний элемент коллекции. Если коллекция пуста, возвращается null, вместо генерации исключения. Подход применим при необходимости безопасного получения элемента с конца без обработки исключений.

Сигнатуры и варианты:

  • E pollLast() - из java.util.Deque. Не принимает аргументов. Возвращает элемент типа E или null, если deque пуст.
  • E pollLast(long timeout, java.util.concurrent.TimeUnit unit) throws InterruptedException - из java.util.concurrent.BlockingDeque. Блокирует поток до появления элемента или до истечения таймаута; при истечении времени возвращает null. Может выбросить InterruptedException.

Поведение и особенности:

  • Метод удаляет элемент из структуры. Для получения без удаления используется peekLast().
  • Возвращаемое null одновременно означает либо пустую коллекцию, либо (в редких реализациях, допускающих null) реальное значение null. Большинство реализаций стандартной библиотеки Java не допускают null как элемент и используют null исключительно для обозначения пустоты.
  • Альтернативы: removeLast() (генерирует NoSuchElementException при пустой коллекции), pollFirst(), peekLast(), блокирующий вариант в BlockingDeque.
  • Поведение в многопоточной среде зависит от конкретной реализации (например, ConcurrentLinkedDeque, LinkedBlockingDeque и другие имеют разную гарантию согласованности и блокировки).

Примеры базового использования

Примеры показывают разные реализации и варианты ответа метода.

1) ArrayDeque: удаление последнего элемента

import java.util.ArrayDeque;
import java.util.Deque;

Deque<String> dq = new ArrayDeque<>();
dq.add("a");
dq.add("b");
String last = dq.pollLast();
System.out.println(last);
System.out.println(dq);
b
[a]

2) Поведение при пустой коллекции

Deque<Integer> empty = new java.util.ArrayDeque<>();
Integer v = empty.pollLast();
System.out.println(v);
null

3) Сравнение с removeLast()

Deque<Integer> d = new java.util.ArrayDeque<>();
System.out.println(d.pollLast());
try {
    d.removeLast();
} catch (Exception e) {
    System.out.println(e.getClass().getSimpleName());
}
null
NoSuchElementException

4) Блокирующий вариант с таймаутом в LinkedBlockingDeque

import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;

LinkedBlockingDeque<String> bq = new LinkedBlockingDeque<>();
String r = bq.pollLast(500, TimeUnit.MILLISECONDS); // ждет 500 мс
System.out.println(r);
null

Похожие методы в Java

  • peekLast() - возвращает последний элемент без удаления; при пустой коллекции возвращает null.
  • removeLast() - удаляет и возвращает последний элемент, но при пустой коллекции генерирует NoSuchElementException. Предпочтительнее при подразумеваемой гарантии непустой коллекции.
  • pollFirst() - аналогично pollLast(), но с начала коллекции.
  • offerLast(E) / addLast(E) - добавление в конец; в блокирующих реализациях есть временные варианты.
  • BlockingDeque.pollLast(long, TimeUnit) - блокирующий вариант с таймаутом; подходит для потребителей в многопоточном обмене.

Выбор зависит от требований: если нежелательна обработка исключений - использовать pollLast(); если пустота считается ошибкой - removeLast(). Для неблокирующего многопоточного доступа выбирается подходящая concurrent-реализация.

Аналоги в других языках

Краткие сравнения и примеры поведения.

  • JavaScript: стандартные массивы используют pop(), который удаляет и возвращает последний элемент, но при пустом массиве возвращает undefined.
const a = [];
console.log(a.pop());
undefined
  • Python: collections.deque.pop() удаляет последний элемент и при пустой коллекции возбуждает IndexError. Для безопасного доступа используется проверка на пустоту или обработка исключения.
from collections import deque
q = deque()
try:
    print(q.pop())
except Exception as e:
    print(type(e).__name__)
IndexError
  • PHP: array_pop() для массивов возвращает null при пустом массиве; класс SplDoublyLinkedList имеет методы pop() и shift().
$a = [];
var_dump(array_pop($a));
NULL
  • C#: нет встроенного Deque в BCL; для похожего поведения используется LinkedList<T> с Last и RemoveLast(). При пустой коллекции доступ к Last даст null у ссылочных типов либо требуется проверка.
var ll = new System.Collections.Generic.LinkedList<int>();
// нужно проверять ll.Last перед удалением
(нет вывода)
  • Go: пакет container/list предоставляет двусвязный список; метод Back() возвращает элемент или nil, а Remove удаляет его.
import (
  "container/list"
  "fmt"
)

l := list.New()
if e := l.Back(); e != nil {
  val := e.Value
  l.Remove(e)
  fmt.Println(val)
} else {
  fmt.Println(nil)
}
<nil>
  • Kotlin: при использовании java.util.Deque вызываются те же методы; в стандартной Kotlin коллекции есть методы вроде removeLastOrNull() (в некоторых версиях) - возвращают null при пустой коллекции.

Ключевое отличие от Java: в некоторых языках (JavaScript, PHP, Go) безопасное значение при пустоте - undefined / null, тогда как Python предпочитает бросать исключение. Это влияет на стиль обработки: проверка на пустоту или ловля исключения.

Типичные ошибки и подводные камни

  • Ошибка: ожидание исключения при пустой коллекции. Многие используют pollLast(), ожидая исключение, но метод возвращает null. Пример:
Deque<String> d = new ArrayDeque<>();
String s = d.pollLast();
if (s == null) {
    System.out.println("пусто");
}
пусто
  • Ошибка: добавление null в реализацию, не допускающую null. Многие реализации, включая ArrayDeque, не допускают null и при попытке добавления выбросят NullPointerException.
Deque<String> dq = new ArrayDeque<>();
try {
    dq.add(null);
} catch (Exception e) {
    System.out.println(e.getClass().getSimpleName());
}
NullPointerException
  • Ошибка: неверная работа в многопоточной среде при использовании небезопасных реализаций. Для параллельных сценариев требуется выбрать concurrent-реализацию или блокирующую очередь.
  • Ошибка: игнорирование InterruptedException у BlockingDeque.pollLast(timeout, unit). Требуется обработка прерываний.

Изменения и совместимость

Сам интерфейс Deque и метод pollLast() существуют в Java достаточно давно и не претерпевали значительных изменений в сигнатуре в последних релизах. Блокирующие варианты находятся в BlockingDeque (java.util.concurrent) и также стабилизированы.

В новых версиях платформы появлялись и появляются улучшенные реализации и оптимизации (например, улучшения производительности ArrayDeque и concurrent-коллекций), но семантика pollLast() сохраняется: извлечение и удаление элемента с конца с возвратом null для пустой коллекции.

Расширенные примеры и нетривиальные сценарии

1) Потребитель-производитель с LinkedBlockingDeque и таймаутом

Пример java
import java.util.concurrent.*;

LinkedBlockingDeque<String> queue = new LinkedBlockingDeque<>();
ExecutorService ex = Executors.newFixedThreadPool(2);

ex.submit(() -> {
    try {
        Thread.sleep(200);
        queue.putLast("job1");
    } catch (InterruptedException ignored) {}
});

ex.submit(() -> {
    try {
        String item = queue.pollLast(1, TimeUnit.SECONDS);
        System.out.println(item);
    } catch (InterruptedException e) {
        System.out.println("interrupted");
    }
});

ex.shutdown();
ex.awaitTermination(2, TimeUnit.SECONDS);
job1

Комментарий: блокирующий вариант удобен для ожидания появления элемента с ограничением по времени и корректной обработкой прерываний.

2) Реализация алгоритма «скользящее максимальное окно» с помощью Deque (удаление с конца при меньших значениях)

Пример java
import java.util.*;

int[] a = {1,3,1,2,0,5};
int k = 3;
Deque<Integer> dq = new ArrayDeque<>(); // хранит индексы
List<Integer> res = new ArrayList<>();

for (int i = 0; i < a.length; i++) {
    while (!dq.isEmpty() && a[dq.peekLast()] <= a[i]) dq.pollLast();
    dq.addLast(i);
    if (dq.peekFirst() <= i - k) dq.pollFirst();
    if (i >= k - 1) res.add(a[dq.peekFirst()]);
}
System.out.println(res);
[3, 3, 2, 5]

Комментарий: здесь pollLast() используется для удаления индексов с конца, чьи значения не полезны для будущих максимумов.

3) Безопасное извлечение из ConcurrentLinkedDeque в многопоточности

Пример java
import java.util.concurrent.ConcurrentLinkedDeque;

ConcurrentLinkedDeque<String> cld = new ConcurrentLinkedDeque<>();
cld.add("x");
cld.add("y");

String e;
while ((e = cld.pollLast()) != null) {
    System.out.println(e);
}
System.out.println(cld.size());
y
x
0

Комментарий: в lock-free структурах pollLast() обеспечивает безопасное неблокирующее извлечение несколькими потоками одновременно.

4) Использование pollLast в реализации кэша LRU (удаление наименее недавно используемого)

Пример java
// Упрощенный пример: двусвязный deque хранит ключи, при переполнении удаляется последний
Deque<String> order = new ArrayDeque<>();
Map<String,String> map = new HashMap<>();
int capacity = 2;

void put(String k, String v) {
    if (map.containsKey(k)) {
        order.remove(k);
    } else if (map.size() >= capacity) {
        String lru = order.pollLast();
        map.remove(lru);
    }
    order.addFirst(k);
    map.put(k, v);
}

// демонстрация
put("a","1");
put("b","2");
put("c","3");
System.out.println(map.keySet());
[c, b]

Комментарий: pollLast() позволяет извлечь и удалить наименее недавно использованный ключ.

джава Deque.pollLast function comments

En
Deque.pollLast Retrieves and removes the last element of this deque, or returns null if this deque is empty