Interface Deque do Java
Operações de fila de duas extremidades em Java com a interface Deque — addFirst, addLast, pollFirst, pollLast.
Deque<E> — pronunciado "deck," abreviação de double-ended queue (fila de duas extremidades) — é um Queue<E> com uma segunda extremidade. Enquanto uma Queue só permite remover da cabeça, uma Deque permite inserir, remover e examinar em ambas as extremidades, cabeça e cauda. Essa única capacidade extra é suficiente para tornar Deque o contrato por trás de duas abstrações completamente diferentes: é uma fila, é uma pilha, e o JDK a recomenda como padrão para ambas.
Duas extremidades × três operações × dois estilos de erro
A interface parece intimidadora no início porque cada operação aparece em doze formas — cabeça vs cauda, inserção vs remoção vs exame, lança exceção vs retorna valor especial. Mas é apenas a grade 3×2 do Queue estendida por duas extremidades:
| Operação | Primeira (cabeça) — lança | Primeira (cabeça) — valor especial | Última (cauda) — lança | Última (cauda) — valor especial |
|---|---|---|---|---|
| Inserção | addFirst(e) | offerFirst(e) | addLast(e) | offerLast(e) |
| Remoção | removeFirst() | pollFirst() | removeLast() | pollLast() |
| Exame | getFirst() | peekFirst() | getLast() | peekLast() |
A coluna "lança" é a que você usa quando uma deque vazia/cheia naquele ponto seria um bug. A coluna "valor especial" é a que você usa quando o estado vazio é esperado e você quer testá-lo em uma condição de loop.
O mapeamento de fila
Deque estende Queue, portanto uma Deque é uma Queue. Os métodos herdados são aliases para as variantes de cabeça-a-cauda:
Método Queue | Equivalente Deque |
|---|---|
add(e) / offer(e) | addLast(e) / offerLast(e) |
remove() / poll() | removeFirst() / pollFirst() |
element() / peek() | getFirst() / peekFirst() |
Inserir na cauda, remover da cabeça — essa é a disciplina FIFO que um Queue já oferece, apenas escrita com os nomes explícitos de cada extremidade.
O mapeamento de pilha
Deque também define três métodos de "pilha" que atuam todos na cabeça:
| Método de pilha | Equivalente Deque |
|---|---|
push(e) | addFirst(e) |
pop() | removeFirst() |
peek() | peekFirst() |
Empilhar, desempilhar — disciplina LIFO, mesma cabeça, extremidade oposta da inserção/remoção em relação ao mapeamento de fila. É por isso que o capítulo sobre Stack indicou você aqui: a forma moderna de escrever uma pilha em Java é Deque<E> stack = new ArrayDeque<>(); e chamar push / pop / peek.
A regra: null não é permitido
Assim como Queue, o contrato de Deque reserva null como sentinela de "vazio" para pollFirst, pollLast, peekFirst, peekLast. Por isso, toda Deque de propósito geral no JDK rejeita elementos null com NullPointerException na inserção. A única exceção é LinkedList, que permite null por razões históricas — e herda toda a ambiguidade que isso implica. Não faça isso.
Iteração e iteração reversa
Uma Deque tem dois iteradores por convenção:
iterator()percorre de cabeça a cauda — a ordem em que você chamariapollFirstrepetidamente.descendingIterator()percorre de cauda a cabeça — a ordem em que você chamariapollLastrepetidamente.
Ambos são tipicamente fail-fast: modificar a deque de fora do iterador durante a iteração lança ConcurrentModificationException. Use Iterator.remove() ou removeIf se precisar modificar durante uma travessia.
Escolhendo uma implementação
Em código de thread única, há essencialmente duas opções:
ArrayDeque— o padrão. Array circular, O(1) em ambas as extremidades, sem alocação de nó por elemento, amigável ao cache. Rejeitanull. ImplementaDequeeQueue.LinkedList— nós duplamente encadeados. Também implementaList, então cada elemento recebe um nó com ponteirosprev/next. Mais lento em ambas as extremidades do queArrayDequee usa mais memória. Use-o apenas se você realmente precisar de semântica deDequeeListno mesmo objeto.
Para código concorrente (abordado mais adiante na parte de concorrência do livro):
ConcurrentLinkedDeque— sem bloqueio, ilimitado.LinkedBlockingDeque— opcionalmente limitado, bloqueante — a versão que você usaria para construir uma fila de roubo de trabalho ou um produtor/consumidor com contrapressão em qualquer extremidade.
Exemplo prático: fila, pilha e verificação de palíndromo em uma deque
O programa abaixo usa um ArrayDeque como fila FIFO, outro como pilha LIFO e um terceiro para verificar se uma frase é um palíndromo, alimentando ambas as extremidades e comparando. O ponto é que a mesma interface, e até a mesma classe, desempenha os três papéis.
O que essa execução demonstra:
- A mesma classe desempenha os papéis de fila e pilha sem renomear nenhum método além de qual extremidade você acessa.
- Os métodos de "retorno de valor especial" produzem silenciosamente
nullpara o caso vazio; os métodos de "lança" levantamNoSuchElementException. Escolha com base em se o estado vazio é esperado ou é um bug. - A verificação de palíndromo usa ambas as extremidades no mesmo loop —
pollFirstepollLastjuntos. Esse é o padrão de acesso que apenas umaDequeoferece com eficiência.
O que vem a seguir
O capítulo sobre Deque encerra o lado de filas do framework. A outra grande abstração em Collection é aquela que rejeita duplicatas: um Set é uma Collection com o contrato de unicidade embutido. Veremos isso a seguir.