W3docs

Interface Queue do Java

Operações de fila FIFO em Java com a interface Queue — offer, poll, peek — e suas implementações.

Queue<E> é Collection<E> mais a ideia de uma cabeça. A cabeça é o próximo elemento a ser removido; todo o resto aguarda sua vez atrás dela. A semântica padrão — e a que quase todas as implementações no JDK usam — é FIFO: primeiro a entrar, primeiro a sair. Produtores usam offer para inserir itens no final; consumidores usam poll para removê-los da cabeça. Este capítulo trata do contrato: os seis métodos, os dois estilos de tratamento de erros, a regra sobre null e qual implementação escolher em cada situação.

Duas formas de fazer cada coisa — por design

Toda operação de fila tem dois sabores: um que lança exceção em caso de falha e outro que retorna um valor especial. O Javadoc apresenta isso como uma grade 3×2:

OperaçãoLança exceção em falhaRetorna valor especial
Inserirboolean add(E e) (lança IllegalStateException se cheia)boolean offer(E e) (retorna false se cheia)
RemoverE remove() (lança NoSuchElementException se vazia)E poll() (retorna null se vazia)
ExaminarE element() (lança NoSuchElementException se vazia)E peek() (retorna null se vazia)

A coluna "lança exceção" é a herdada de Collection e List — é familiar, mas inconveniente quando o caso vazio/cheio é normal (consumidor aguardando o próximo trabalho, fila limitada rejeitando um produtor em excesso). A coluna "retorna valor especial" foi adicionada para filas, permitindo escrever loops que não dependem de exceções para controle de fluxo:

String item;
while ((item = queue.poll()) != null) {
  process(item);
}

Use a forma de retorno por padrão. Recorra a remove/element apenas quando uma fila vazia naquele ponto seria um erro genuíno que você deseja expor como exceção.

Null e Queue não se misturam

Como poll() e peek() retornam null para indicar "vazia", permitir null como elemento real é uma receita para ambiguidade. Toda Queue de uso geral no JDK, exceto LinkedList, rejeita nullArrayDeque, PriorityQueue, ArrayBlockingQueue, todas as filas concorrentes. LinkedList permite adicionar null, mas se você fizer isso terá quebrado o contrato: não há mais como distinguir "a cabeça é null" de "a fila está vazia".

A regra: não armazene null em uma fila. Escolha ArrayDeque em vez de LinkedList e a linguagem impõe isso por você.

FIFO é o padrão — mas não é universal

Queue não exige FIFO. A interface define uma cabeça e operações que removem dela; como a cabeça é determinada cabe à implementação. As duas implementações em java.util que não são FIFO são:

  • PriorityQueue — a cabeça é sempre o menor elemento (por ordem natural ou um Comparator). As inserções vão onde o heap determina, não no final.
  • As variantes bloqueantes em java.util.concurrentPriorityBlockingQueue, DelayQueue, etc. — reordenam por prioridade, prazo, etc.

Todo o restante (ArrayDeque, LinkedList, ArrayBlockingQueue, LinkedBlockingQueue, ConcurrentLinkedQueue) é FIFO.

Escolhendo uma implementação

Para o caso single-threaded:

  • ArrayDeque — o padrão. Array circular, sem alocação por elemento, rápido. Use como Queue<E> ou como Deque<E> dependendo de precisar acessar ambas as extremidades.
  • LinkedList — funciona, também implementa List. Escolha apenas se precisar genuinamente de ambas as interfaces no mesmo objeto. O capítulo sobre LinkedList cobre as trocas.
  • PriorityQueue — quando você quer "menor item aguardando a seguir" em vez de FIFO.

Para o caso multi-threaded (coberto em detalhe na parte de concorrência mais adiante — apenas referências aqui):

  • ConcurrentLinkedQueue — FIFO sem bloqueio. Ilimitada.
  • ArrayBlockingQueue — limitada, respaldada por array, bloqueia put quando cheia e take quando vazia. A fila clássica produtor/consumidor.
  • LinkedBlockingQueue — opcionalmente limitada, forma de nó encadeado da mesma.
  • PriorityBlockingQueuePriorityQueue concorrente. Ilimitada.

A maioria dos códigos de aplicação usa uma das quatro primeiras. As filas bloqueantes são como você constrói pools de trabalhadores e controle de fluxo.

O que você pode chamar em qualquer Queue

Além dos seis métodos específicos de cabeça, uma Queue também é uma Collection — portanto size, isEmpty, contains, iterator, forEach, stream, clear, toArray funcionam. A ordem de iteração é a ordem em que os elementos seriam removidos por poll nas implementações FIFO (ArrayDeque, LinkedList), mas para PriorityQueue a ordem do iterador não é a ordem de prioridade — ele visita o array de heap subjacente como está. Se quiser os elementos em ordem de prioridade, você precisa esvaziar a fila com poll até que esteja vazia.

Um exemplo prático: produtor/consumidor em uma thread

O programa abaixo usa um ArrayDeque como fila FIFO para modelar um pequeno sistema de impressão: trabalhos chegam ao longo do tempo (representamos isso usando offer em lotes), e o trabalhador esvazia cada lote com poll. O par de estilos de erro é mostrado no topo para que você veja exatamente quando cada forma é acionada.

java— editable, runs on the server

Alguns pontos a observar na execução:

  • poll e peek retornaram silenciosamente null quando a fila estava vazia; remove e element lançaram exceções. Ambos os comportamentos fazem parte do contrato — escolha o que combina com o fato de "vazio" ser um erro ou apenas um estado.
  • offer(null) lançou exceção em ArrayDeque. É a implementação impondo a regra da qual a interface depende.
  • O PriorityQueue removeu 10, 20, 30, 40 — ordem classificada, não ordem de inserção. Mesma interface Queue, regra de seleção de cabeça completamente diferente.

O que vem a seguir

A primeira implementação não-FIFO merece seu próprio capítulo — PriorityQueue é uma fila respaldada por heap onde a cabeça é sempre o menor elemento. Esse é o ingrediente básico de praticamente todo agendador de "processar a tarefa mais urgente a seguir" no JDK.

Prática

Prática
Dentro de um loop você escreve `while ((job = queue.poll()) != null) { process(job); }`. Se usasse `queue.remove()` no lugar, o que mudaria no nível do contrato?
Dentro de um loop você escreve `while ((job = queue.poll()) != null) { process(job); }`. Se usasse `queue.remove()` no lugar, o que mudaria no nível do contrato?
Was this page helpful?