Java Stack
Estruturas de dados LIFO em Java — a classe Stack legada e a alternativa moderna baseada em ArrayDeque.
Uma pilha (stack) é uma coleção último-a-entrar-primeiro-a-sair: você usa push para adicionar um item e o próximo pop retorna o que foi colocado mais recentemente. O Java tem duas formas de criar uma — a classe legada java.util.Stack de 1995 e a recomendação moderna de Deque<E> (normalmente ArrayDeque). Ambas funcionam, ambas têm as mesmas operações conceituais, mas o próprio Javadoc oficial do Stack orienta a preferir o Deque. Este capítulo mostra como o Stack funciona, por que o JDK agora aponta para outra direção e como usar o substituto recomendado.
Para que serve uma pilha
Pilhas aparecem em qualquer algoritmo que seja naturalmente último-a-entrar-primeiro-a-sair:
- Históricos de desfazer/refazer — cada nova ação é empilhada com push; desfazer retira a mais recente com pop.
- Estado de parser e interpretador — avaliação de expressões, verificação de colchetes balanceados, frames de chamada.
- Travessia em profundidade — empilha os sucessores, retira com pop o próximo nó a visitar.
- Inversão de sequências — empilha cada elemento de uma sequência e depois os retira com pop.
A forma é sempre a mesma: push, pop, peek, isEmpty, size. Cinco operações.
A classe Stack legada
java.util.Stack<E> estende Vector<E>, o que significa que herda tudo do capítulo anterior — incluindo o synchronized por método, os nomes de métodos legados e o fato desconfortável de ser uma List. Um Stack é, por herança, uma lista indexável — você pode chamar stack.get(3) nele, que é exatamente o tipo de erro de API que motiva a recomendação de usar Deque.
Stack<String> s = new Stack<>();
s.push("a");
s.push("b");
s.push("c");
System.out.println(s.peek()); // c
System.out.println(s.pop()); // c
System.out.println(s.size()); // 2
System.out.println(s.empty()); // false (note: empty(), not isEmpty())Seis métodos específicos do Stack:
E push(E item)— adiciona ao topo. Retorna o item.E pop()— remove e retorna o topo. LançaEmptyStackExceptionse estiver vazio.E peek()— topo sem remover. Mesma exceção.boolean empty()— note a grafia em minúsculas, distinta deCollection.isEmpty().int search(Object o)— distância baseada em 1 a partir do topo, ou-1se não presente. Escolha estranha — o resultado é "quantospops para alcançaro", o que raramente é útil.
Todo o restante é herdado de Vector e List.
Por que o Javadoc aponta para Deque
Abra o Javadoc real do Stack em qualquer JDK moderno e você encontrará a linha:
A more complete and consistent set of LIFO stack operations is provided by the
Dequeinterface and its implementations, which should be used in preference to this class.
Os motivos se alinham com o capítulo anterior sobre Vector:
Stacké sincronizado em cada método. Você paga o custo do lock mesmo em código de thread único, e a sincronização está na granularidade errada para qualquer operação composta.Stacké umaList. Acesso indexado em uma pilha é um erro de categoria — permite espiar o meio, o que viola a disciplina LIFO que a classe deveria impor.- As cinco operações de pilha que você realmente quer já estão em
Deque(push,pop,peek,isEmpty,size), eArrayDequeé a implementação mais rápida no JDK.
Em resumo: Stack funciona, mas é um design de 1995 encaixado num framework de 1998. Código novo deve usar Deque.
O substituto recomendado
O idioma é uma linha:
Deque<String> stack = new ArrayDeque<>();Depois chame push, pop, peek. A interface Deque os define internamente como addFirst, removeFirst, peekFirst, então o topo da pilha é a cabeça do deque.
Deque<String> calls = new ArrayDeque<>();
calls.push("main");
calls.push("parseArgs");
calls.push("split");
System.out.println(calls.peek()); // split
System.out.println(calls.pop()); // split
System.out.println(calls); // [parseArgs, main]Três detalhes a saber:
peek()em umDequeretornanullquando vazio;peek()emStacklança exceção.peekFirst()é a mesma forma que retorna null. Se preferir a exceção, chamegetFirst()(ouelement()).- A mesma distinção
pop()/removeFirst()se aplica:pop()lança exceção quando vazio. ArrayDequerejeita elementosnull. Use-o apenas para valores não nulos — que é o caso usual para pilhas.
Quando Stack é aceitável em código novo
Quase nunca, e a barra é baixa:
- Manutenção de código antigo que já o usa — não faça refatoração só para trocar a classe.
- Trabalhar com uma API que exige
Stackespecificamente — extremamente raro. As classes Swing que usavamVectore similares não tendem a exigirStack.
Para tudo mais, declare Deque<E> e instancie ArrayDeque<>.
Um exemplo prático: verificador de parênteses, de duas formas
O programa abaixo usa tanto Stack quanto Deque<Character> para resolver o clássico problema dos parênteses balanceados. Mesmo algoritmo, duas implementações — leia lado a lado e depois olhe a forma com Deque e decida qual preferiria ler daqui a três meses.
O que vale notar:
- As duas funções são idênticas exceto pelo nome do tipo e
empty()vsisEmpty(). Não há razão algorítmica para preferirStack— o código lê-se da mesma forma. - O bloco "Stack-specific API" no final é o argumento contra a classe legada:
empty()tem um nome diferente do restante do framework,search()retorna uma distância baseada em 1 (raro no resto do Java) eget(0)permite acessar o meio de uma pilha — derrotando todo o propósito da abstração.
O que vem a seguir
Você já viu as três implementações List mais usadas (ArrayList, LinkedList, Vector) e a especialização LIFO construída sobre Vector. Os próximos quatro capítulos cobrem a história paralela para coisas-que-você-adiciona-e-retira-em-ordem: a interface Queue, PriorityQueue, ArrayDeque e o mais geral contrato Deque.