1. Interface Queue: fila clássica (FIFO)
Em programação, como na vida, há situações em que não basta armazenar um conjunto de elementos, mas é importante controlar a ordem de processamento. Imagine uma fila no supermercado: quem está mais perto do início é atendido primeiro. Esse é o princípio FIFO — “primeiro a entrar — primeiro a sair”. Já uma pilha de pratos no armário funciona segundo o princípio LIFO — “último a entrar — primeiro a sair”. Em Java, esses princípios correspondem às coleções: filas Queue, filas de duas pontas Deque e pilhas Stack.
Onde se aplicam:
- Processamento de tarefas por ordem de chegada (fila de tarefas, impressão de documentos, processamento de eventos).
- Implementação de desfazer/refazer (undo/redo) — pilha.
- Análise de expressões, travessia de árvores e grafos (busca em largura/profundidade) e muito mais.
O que é uma fila?
Uma fila (Queue) é uma coleção baseada no princípio FIFO. Os elementos são adicionados ao final e removidos do início. Como na loja: quem chegou primeiro é atendido primeiro.
Métodos principais da interface Queue
| Método | O que faz | Retorno |
|---|---|---|
|
Adiciona o elemento ao final (sem exceção) | true/false |
|
Adiciona o elemento ao final (lança exceção) | true/Exception |
|
Remove e retorna o primeiro elemento | elemento/null |
|
Remove e retorna o primeiro elemento | elemento/Exception |
|
Retorna o primeiro elemento, sem remover | elemento/null |
|
Retorna o primeiro elemento, sem remover | elemento/Exception |
Você deve ter notado pares de métodos com comportamento semelhante. A diferença está na “suavidade”: offer, poll, peek retornam um valor especial em caso de falha (geralmente null ou false) e não lançam exceções, enquanto add, remove, element lançam exceções em situações de borda (fila vazia, às vezes estouro).
Exemplo: fila de tarefas
import java.util.*;
public class QueueDemo {
public static void main(String[] args) {
Queue<String> tasks = new LinkedList<>();
tasks.offer("Escovar os dentes");
tasks.offer("Fazer exercícios");
tasks.offer("Tomar café");
while (!tasks.isEmpty()) {
String task = tasks.poll(); // Pegamos a tarefa do início da fila
System.out.println("Executando: " + task);
}
}
}
Saída:
Executando: Escovar os dentes
Executando: Fazer exercícios
Executando: Tomar café
Por que o LinkedList é usado com frequência?
Uma interface é um contrato, e há várias implementações. Muitas vezes usa-se LinkedList, pois ele adiciona/remove rapidamente em ambas as pontas. No entanto, uma ótima alternativa é ArrayDeque. Há também variantes especializadas: PriorityQueue (fila por prioridades), sobre a qual falaremos abaixo.
2. Interface Deque: fila de duas pontas
Deque (Double Ended Queue, “deque”) é uma fila em que é possível adicionar e remover elementos em ambas as pontas: do início e do fim. Como um ônibus com duas portas: dá para entrar/sair de qualquer lado.
Deque = um verdadeiro coringa: pode funcionar como fila (FIFO), como pilha (LIFO) e como híbrido.
Métodos principais da interface Deque
| Método | O que faz | Exemplo de uso |
|---|---|---|
|
Adicionar no início | |
|
Adicionar no fim | |
|
Remover e retornar do início | |
|
Remover e retornar do fim | |
|
Olhar o início (sem remover) | |
|
Olhar o fim (sem remover) | |
|
Adicionar no início (sem exceção) | |
|
Adicionar no fim (sem exceção) | |
Exemplo: Deque como fila e como pilha
import java.util.*;
public class DequeDemo {
public static void main(String[] args) {
Deque<String> deque = new ArrayDeque<>();
// Usando como fila (FIFO)
deque.offerLast("A");
deque.offerLast("B");
deque.offerLast("C");
System.out.println("Fila (FIFO):");
while (!deque.isEmpty()) {
System.out.println(deque.pollFirst());
}
// Usando como pilha (LIFO)
deque.offerLast("1");
deque.offerLast("2");
deque.offerLast("3");
System.out.println("Pilha (LIFO):");
while (!deque.isEmpty()) {
System.out.println(deque.pollLast());
}
}
}
Saída:
Fila (FIFO):
A
B
C
Pilha (LIFO):
3
2
1
Por que ArrayDeque?
ArrayDeque é uma implementação Deque baseada em array, rápida e compacta. Para pilha e fila, ela costuma ser mais rápida e confiável do que a antiga Stack, e não tem tamanho fixo (limitada apenas pela memória).
3. Stack: pilha — último a entrar, primeiro a sair (LIFO)
A pilha é uma coleção segundo o princípio LIFO: removemos o topo sendo o último elemento adicionado. Em programação, a pilha é útil para histórico de ações (undo), travessia de estruturas recursivas (árvores/grafos), avaliação de expressões etc.
Classe Stack (e por que não usá-la)
Em Java existe a classe Stack, que herda da obsoleta Vector. Hoje não se recomenda usá-la em novos projetos. Prefira Deque/ArrayDeque.
Métodos de pilha (Deque)
| Método | O que faz |
|---|---|
|
Colocar o elemento no topo da pilha |
|
Remover e retornar o elemento do topo |
|
Olhar o elemento do topo |
Atenção: em Deque essas operações correspondem aos métodos addFirst/removeFirst/peekFirst, mas por compatibilidade também existem os nomes “de pilha” push/pop/peek.
Exemplo: pilha com ArrayDeque
import java.util.*;
public class StackDemo {
public static void main(String[] args) {
Deque<String> stack = new ArrayDeque<>();
stack.push("Primeiro");
stack.push("Segundo");
stack.push("Terceiro");
while (!stack.isEmpty()) {
System.out.println(stack.pop());
}
}
}
Saída:
Terceiro
Segundo
Primeiro
Por que não Stack?
- Stack é sincronizada (mais lenta) e herda a obsoleta Vector.
- ArrayDeque costuma ser mais rápida e compacta, não bloqueia threads.
- Se você vir Stack em código novo — geralmente é candidata a substituição por ArrayDeque.
4. Quando usar o quê
| Estrutura | Princípio | Onde usar | Exemplo de implementação |
|---|---|---|---|
| Queue | FIFO | Fila de tarefas, processamento de eventos, impressão, filas de clientes | |
| Stack | LIFO | Undo/redo, recursão, parsers, travessia de árvores | |
| Deque | FIFO/LIFO | Buffers, palíndromos, filas de duas pontas, tarefas gerais | |
Exemplos do dia a dia:
- Fila no banco — Queue.
- Histórico de ações no editor — Stack.
- Fila de ônibus, entrada/saída por ambas as pontas — Deque.
5. Exemplos de código: implementando fila e pilha em um miniaplicativo
Exemplo 1: fila de impressão de documentos
import java.util.*;
public class PrintQueueApp {
public static void main(String[] args) {
Queue<String> printQueue = new ArrayDeque<>();
printQueue.offer("Documento1.pdf");
printQueue.offer("Documento2.docx");
printQueue.offer("Documento3.xls");
while (!printQueue.isEmpty()) {
String doc = printQueue.poll();
System.out.println("Imprimindo: " + doc);
}
}
}
Exemplo 2: pilha de desfazer ações (undo)
import java.util.*;
public class UndoStackApp {
public static void main(String[] args) {
Deque<String> undoStack = new ArrayDeque<>();
undoStack.push("Inserção de texto");
undoStack.push("Alteração de cor");
undoStack.push("Exclusão de imagem");
System.out.println("Última ação: " + undoStack.peek()); // Exclusão de imagem
while (!undoStack.isEmpty()) {
System.out.println("Desfazer: " + undoStack.pop());
}
}
}
Exemplo 3: fila de duas pontas
import java.util.*;
public class DequeApp {
public static void main(String[] args) {
Deque<String> deque = new ArrayDeque<>();
deque.addFirst("A");
deque.addLast("B");
deque.addFirst("C");
deque.addLast("D");
System.out.println("Removendo do início: " + deque.removeFirst()); // C
System.out.println("Removendo do fim: " + deque.removeLast()); // D
System.out.println("Restou: " + deque); // [A, B]
}
}
6. Particularidades de implementação e nuances
- ArrayDeque — a implementação de fila/pilha mais rápida e versátil para a maioria das tarefas.
- LinkedList — também implementa Deque; pode ser útil, mas para filas curtas geralmente é mais lento que ArrayDeque.
- PriorityQueue — fila por prioridade: a ordem é determinada pela prioridade (natural ou via Comparator), não é uma fila FIFO comum.
- Stack — obsoleta e sincronizada; em novos projetos, prefira ArrayDeque.
- Deque é conveniente por permitir trabalhar tanto com o início quanto com o fim da estrutura, cobrindo cenários de filas e pilhas com uma única abstração.
7. Erros típicos ao trabalhar com filas e pilhas
Erro nº 1: Usar Stack em vez de Deque.
Muitos iniciantes veem a classe Stack e a escolhem “pelo nome”. Em projetos modernos, em vez dela usa-se ArrayDeque (ou LinkedList) com os métodos push/pop.
Erro nº 2: Quebrar o princípio de funcionamento da estrutura.
Tentam acessar elementos da fila/pilha por índice, por exemplo queue.get(0) ou stack.get(0). Não pode — use os métodos apropriados de filas e pilhas (peek, poll, pop etc.).
Erro nº 3: Usar remove()/element()/add() sem verificação.
Os métodos remove, element, add podem lançar exceção quando a estrutura está vazia/cheia. É mais seguro usar os “suaves” offer, poll, peek, que retornam valores especiais.
Erro nº 4: Usar ArrayDeque com elementos null.
ArrayDeque não permite null. Tentar adicionar null causará NullPointerException.
Erro nº 5: Usar PriorityQueue como uma fila comum.
Em PriorityQueue, a ordem é determinada por prioridades (naturais ou definidas via Comparator). Para um FIFO estrito, use ArrayDeque ou LinkedList.
Erro nº 6: Modificar a fila/pilha durante um for-each.
Remover elementos da coleção durante o for-each pode levar a ConcurrentModificationException. Para remoção, use o iterador ou métodos poll/pop em loop, como mostrado nos exemplos.
GO TO FULL VERSION