Visão Geral do Java Collections Framework
Tour pelo Java Collections Framework — interfaces, implementações e algoritmos em java.util.
Quase todo programa acaba precisando guardar um grupo de coisas — pedidos aguardando envio, palavras de um documento, usuários online no momento, a fila de tarefas pendentes que uma thread trabalhadora vai processar. A resposta do Java para "onde coloco esse grupo" é o Collections Framework: um conjunto coordenado de interfaces em java.util, mais de uma dúzia de implementações por trás delas, e uma única classe utilitária de algoritmos (Collections) que opera sobre as interfaces, não sobre nenhuma implementação específica. Este capítulo é o mapa — o que há no framework, por que ele foi construído assim, e qual família usar em cada situação. Cada capítulo desta parte aprofunda um dos blocos desse mapa.
Por que um framework e não apenas classes
Antes do Java 1.2 existiam classes para grupos (Vector, Hashtable, Stack), mas não havia interface compartilhada — não era possível escrever um método que aceitasse "qualquer lista" e recebesse as três. O Collections Framework resolveu isso separando o que é um grupo (a interface) de como ele é armazenado (a implementação). O ganho fica evidente no dia a dia:
// You program against the interface, not the class.
List<String> names = new ArrayList<>();
// Later, swap in a different implementation without touching the rest of the code:
List<String> names = new LinkedList<>();Todo método chamado em names é definido na interface List. A escolha entre ArrayList e LinkedList é uma decisão de desempenho, não de API. Também é possível escrever métodos como void print(List<String> xs) uma única vez e passar qualquer implementação.
As quatro interfaces raiz
O framework é organizado em torno de quatro interfaces raiz. Escolha aquela cujo contrato corresponde ao que seus dados precisam:
Collection<E>— a raiz. Um grupo de elementosE. Nada é dito sobre ordem, duplicatas ou indexação.List<E>— uma coleção ordenada acessível por índice. Duplicatas permitidas. Pense em "array que cresce" ou "sequência encadeada."ArrayList,LinkedList,Vector.Set<E>— uma coleção que proíbe duplicatas. Conjunto matemático. Pode ou não ser ordenado.HashSet,LinkedHashSet,TreeSet.Map<K, V>— busca por chave→valor. Não é umaCollection(seus elementos são entradas, não valores simples), mas faz parte do framework.HashMap,LinkedHashMap,TreeMap.
Mais duas ficam ao lado de List e Set como especializações de Collection:
Queue<E>— coleção de "próximo da fila". FIFO por padrão.LinkedList,ArrayDeque,PriorityQueue.Deque<E>— fila de duas pontas. Adiciona e remove em qualquer extremidade.ArrayDeque,LinkedList.
Toda classe de coleção da biblioteca padrão implementa ao menos uma dessas interfaces.
Escolha a família pelo comportamento, depois a classe pelo custo
A decisão tem duas camadas:
- O que os dados precisam? Ordenado com duplicatas →
List. Sem duplicatas →Set. Busca por chave →Map. Produtor/consumidor →QueueouDeque. - Dentro da família, quais são os padrões de acesso? Acesso aleatório por índice →
ArrayList. Muitas inserções/remoções na frente →LinkedListouArrayDeque. Iteração ordenada →TreeSet/TreeMap. Simplesmente "preciso de um conjunto rápido" →HashSet/HashMap.
Uma tabela de referência rápida para os principais casos:
| Necessidade | Use | Notas |
|---|---|---|
| Lista redimensionável, acesso aleatório rápido | ArrayList | O List padrão. |
| Lista com inserção/remoção frequente nas extremidades | ArrayDeque (como fila similar a lista) | Supera LinkedList na prática. |
| Set, contains/add/remove rápidos | HashSet | Sem garantia de ordem. |
| Set com iteração previsível | LinkedHashSet | Iteração em ordem de inserção. |
| Set ordenado | TreeSet | Operações O(log n). |
| Map, busca rápida | HashMap | O Map padrão. |
| Map com iteração previsível | LinkedHashMap | Útil para caches (LRU). |
| Map ordenado | TreeMap | Chaves em ordem crescente. |
| Fila FIFO | ArrayDeque | Mais rápido que LinkedList. |
| Sempre remover o menor elemento | PriorityQueue | Baseado em heap. |
Vector, Stack e Hashtable são as classes pré-1.2 — ainda no JDK por compatibilidade retroativa, mas não são mais a escolha certa em código novo. Seus substitutos modernos são abordados em capítulos próprios.
Generics tornam as coleções seguras em tipos
Toda interface e classe de coleção é genérica. Você a parametriza com o tipo do elemento na declaração, e o compilador passa a garantir isso:
List<String> names = new ArrayList<>();
names.add("Ada");
names.add(42); // ❌ compile error — Integer is not a String
String first = names.get(0); // no cast neededO diamante <> à direita pede ao compilador que infira o tipo a partir do lado esquerdo — economiza digitação sem perder segurança. A parte anterior do livro (Generics) é o conjunto de regras por trás de tudo isso; o restante desta parte pressupõe que você a leu.
Os algoritmos vivem na classe Collections
Operações que não estão vinculadas a uma implementação específica — sort, shuffle, reverse, binary-search, min, max, frequency, wrappers não modificáveis — vivem na classe utilitária estática java.util.Collections. Atenção ao s no final: Collection (a interface) é um elemento; Collections (a classe) é o kit de ferramentas:
List<Integer> xs = new ArrayList<>(List.of(3, 1, 4, 1, 5, 9, 2, 6));
Collections.sort(xs); // mutates xs
int idx = Collections.binarySearch(xs, 5);
List<Integer> ro = Collections.unmodifiableList(xs);Dedicamos três capítulos no final desta parte a Collections — busca, ordenação e wrappers não modificáveis — porque a classe utilitária é onde grande parte do trabalho prático acontece.
Um passeio completo inicial
O programa abaixo seleciona um representante de cada família e demonstra as operações que você usará repetidamente. Não se preocupe em entender cada linha agora — cada classe aqui tem seu próprio capítulo. O objetivo é ver a forma do framework: mesmos nomes de métodos, mesmos padrões, mesmo modelo de iteração.
Observe três coisas na saída:
- O
Setsilenciosamente descartou a duplicata"Effective Java". Esse é o contrato — nenhum código extra é necessário da sua parte. - O
Queueretornou os itens na ordem em que foram adicionados.offerenfileira,peekolha o topo sem remover,pollremove e retorna. - O loop
for-eachfunciona em todaCollection. Maps iteramos viaentrySet(),keySet()ouvalues(), dependendo do que você quer.
Esse é o framework em miniatura.
HashSet e HashMap não fazem nenhuma promessa sobre a ordem de iteração — a ordem que você vê na saída acima é um detalhe de implementação e pode variar entre versões da JVM ou execuções. Se você precisar de uma ordem estável, use LinkedHashSet/LinkedHashMap (ordem de inserção) ou TreeSet/TreeMap (ordem crescente). Nunca escreva código que dependa da ordem de iteração de um HashSet ou HashMap simples.
O que vem a seguir
Você viu o mapa. O restante da Parte 11 percorre cada região. O ponto de partida natural é a raiz da qual toda coleção herda — a própria interface Collection, onde métodos como add, remove, contains, size e iterator() são definidos de uma vez por todas.