Dominando Collections em Java: De ArrayList a Estruturas Especializadas
- #Java
Introdução
Collections são o coração de qualquer aplicação Java. Elas permitem armazenar, manipular e recuperar dados de forma eficiente. Mas muitos desenvolvedores limitam-se a ArrayList e HashMap, perdendo oportunidades de usar estruturas especializadas que resolvem problemas de forma elegante.
👉 Código : Acessar o cÓdigo pratico com o uso de collections e suas aplicações -- > CLIQUE AQUI
Neste artigo, exploraremos as Collections mais poderosas e versáteis do Java, com exemplos práticos que você pode aplicar imediatamente em seus projetos.
1. Hierarquia das Collections
Entender a hierarquia é fundamental para escolher a estrutura certa.
Collection
├── List (ordenada, permite duplicatas)
│ ├── ArrayList (busca rápida, inserção lenta)
│ ├── LinkedList (inserção rápida, busca lenta)
│ └── CopyOnWriteArrayList (thread-safe)
├── Set (sem duplicatas)
│ ├── HashSet (não ordenado, rápido)
│ ├── LinkedHashSet (ordem de inserção)
│ └── TreeSet (ordenado, lento)
└── Queue (fila, FIFO)
├── PriorityQueue (heap, ordenado por prioridade)
└── Deque (fila dupla)
Map (chave-valor)
├── HashMap (não ordenado, rápido)
├── LinkedHashMap (ordem de inserção)
├── TreeMap (ordenado por chave)
└── ConcurrentHashMap (thread-safe)
2. List: Quando Usar Cada Uma ArrayList — Acesso Rápido
Use quando você precisa acessar elementos frequentemente.
java
List<String> nomes = new ArrayList<>();
nomes.add("João");
nomes.add("Maria");
nomes.add("Carlos");
// Acesso rápido: O(1)
System.out.println(nomes.get(0)); // "João"
// Inserção no meio é lenta: O(n)
nomes.add(1, "Ana"); // Insere na posição 1
Saída:
João
Complexidade:
- Acesso: O(1)
- Inserção: O(n)
- Remoção: O(n)
LinkedList — Inserção Rápida
Use quando você insere/remove frequentemente no início ou meio.
java
List<String> fila = new LinkedList<>();
fila.add("Primeiro");
fila.add("Segundo");
fila.add("Terceiro");
// Inserção no início: O(1)
fila.addFirst("Zero");
// Remoção do início: O(1)
fila.removeFirst();
// Acesso é mais lento: O(n)
System.out.println(fila.get(1));
Saída:
Segundo
Complexidade:
- Acesso: O(n)
- Inserção: O(1)
- Remoção: O(1)
Comparação Prática
java
// ✅ Use ArrayList para buscas frequentes
List<Integer> numeros = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
for (int i = 0; i < 1000000; i++) {
numeros.get(i % numeros.size()); // Muito rápido
}
// ✅ Use LinkedList para inserções no início
List<String> historico = new LinkedList<>();
for (int i = 0; i < 10000; i++) {
historico.addFirst("Evento " + i); // Muito rápido
}
3. Set: Eliminando Duplicatas HashSet — Rápido e Sem Ordem
java
Set<String> emails = new HashSet<>();
emails.add("joao@email.com");
emails.add("maria@email.com");
emails.add("joao@email.com"); // Duplicata ignorada
System.out.println(emails.size()); // 2
// ✅ Perfeito para verificar existência
if (emails.contains("joao@email.com")) {
System.out.println("Email existe!");
}
Saída:
2
Email existe!
Casos de uso: Cache, verificação de existência, remover duplicatas.
LinkedHashSet — Mantém Ordem
java
Set<String> ordem = new LinkedHashSet<>();
ordem.add("C");
ordem.add("A");
ordem.add("B");
// Itera na ordem de inserção
ordem.forEach(System.out::println); // C, A, B
Saída:
C
A
B
TreeSet — Ordenado Automaticamente
java
Set<Integer> numeros = new TreeSet<>();
numeros.add(5);
numeros.add(1);
numeros.add(3);
numeros.add(2);
// Itera em ordem natural (crescente)
numeros.forEach(System.out::println); // 1, 2, 3, 5
// ✅ Operações de intervalo
NavigableSet<Integer> intervalo = ((TreeSet<Integer>) numeros).subSet(2, true, 4, true);
System.out.println(intervalo); // [2, 3]
Saída:
1
2
3
5
[2, 3]
4. Map: Estrutura Chave-Valor HashMap — Rápido e Flexível
java
Map<String, Integer> salarios = new HashMap<>();
salarios.put("João", 3000);
salarios.put("Maria", 4000);
salarios.put("Carlos", 3500);
// Acesso por chave: O(1)
System.out.println(salarios.get("Maria")); // 4000
// Verificar existência
if (salarios.containsKey("João")) {
System.out.println("João existe!");
}
// Iterar
salarios.forEach((nome, salario) ->
System.out.println(nome + ": R$ " + salario)
);
Saída:
4000
João existe!
Carlos: R$ 3500
João: R$ 3000
Maria: R$ 4000
LinkedHashMap — Mantém Ordem de Inserção
java
Map<String, String> historico = new LinkedHashMap<>();
historico.put("primeiro", "dados");
historico.put("segundo", "dados");
historico.put("terceiro", "dados");
// Itera na ordem de inserção
historico.forEach((chave, valor) -> System.out.println(chave));
// Saída: primeiro, segundo, terceiro
Saída:
primeiro
segundo
terceiro
TreeMap — Ordenado por Chave
java
Map<String, Integer> rankingPalavras = new TreeMap<>();
rankingPalavras.put("zebra", 5);
rankingPalavras.put("apple", 10);
rankingPalavras.put("banana", 7);
// Itera em ordem alfabética
rankingPalavras.forEach((palavra, freq) ->
System.out.println(palavra + ": " + freq)
);
// Saída: apple, banana, zebra
Saída:
apple: 10
banana: 7
zebra: 5
ConcurrentHashMap — Thread-Safe
Use em aplicações multi-thread:
java
Map<String, Integer> contador = new ConcurrentHashMap<>();
// Seguro para múltiplas threads
new Thread(() -> contador.put("thread1", 1)).start();
new Thread(() -> contador.put("thread2", 2)).start();
5. Queue e Deque: Estruturas Especializadas Queue (FIFO)
java
Queue<String> fila = new LinkedList<>();
fila.add("primeiro");
fila.add("segundo");
fila.add("terceiro");
// Remover do início (FIFO)
System.out.println(fila.poll()); // "primeiro"
System.out.println(fila.poll()); // "segundo"
Saída:
primeiro
segundo
PriorityQueue — Ordenado por Prioridade
java
PriorityQueue<Integer> heap = new PriorityQueue<>();
heap.add(5);
heap.add(1);
heap.add(3);
// Sempre retorna o menor
System.out.println(heap.poll()); // 1
System.out.println(heap.poll()); // 3
System.out.println(heap.poll()); // 5
// Com comparador customizado (maior primeiro)
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Collections.reverseOrder());
maxHeap.add(5);
maxHeap.add(1);
maxHeap.add(3);
System.out.println(maxHeap.poll()); // 5
Saída:
1
3
5
5
Deque (Fila Dupla)
java
Deque<String> deque = new LinkedList<>();
deque.addFirst("primeiro");
deque.addLast("último");
deque.addFirst("novo primeiro");
System.out.println(deque.removeFirst()); // "novo primeiro"
System.out.println(deque.removeLast()); // "último"
Saída:
novo primeiro
último
6. Exemplo Completo: Sistema de Cache LRU
java
public class CacheLRU<K, V> extends LinkedHashMap<K, V> {
private int capacidade;
public CacheLRU(int capacidade) {
// LinkedHashMap com ordem de acesso (LRU)
super(capacidade, 0.75f, true);
this.capacidade = capacidade;
}
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > capacidade;
}
public static void main(String[] args) {
CacheLRU<String, String> cache = new CacheLRU<>(3);
cache.put("página1", "conteúdo1");
cache.put("página2", "conteúdo2");
cache.put("página3", "conteúdo3");
System.out.println("Cache após 3 inserts: " + cache.keySet());
// [página1, página2, página3]
cache.get("página1"); // Acesso move para final (LRU)
cache.put("página4", "conteúdo4"); // Remove página2 (menos recente)
System.out.println("Cache após remover LRU: " + cache.keySet());
// [página1, página3, página4]
}
}
Saída:
Cache após 3 inserts: [página1, página2, página3]
Cache após remover LRU: [página1, página3, página4]
7. Conversões Entre Collections
java
// Array para List
String[] array = {"A", "B", "C"};
List<String> lista = Arrays.asList(array);
// List para Array
List<String> lista2 = new ArrayList<>(Arrays.asList("X", "Y", "Z"));
String[] array2 = lista2.toArray(new String[0]);
// Collection para Set (remove duplicatas)
List<Integer> comDuplicatas = Arrays.asList(1, 1, 2, 2, 3);
Set<Integer> semDuplicatas = new HashSet<>(comDuplicatas);
// Unmodifiable Collections
List<String> imutavel = Collections.unmodifiableList(lista);
// imutavel.add("D"); // Lança UnsupportedOperationException
Mostrar Imagem
8. Performance: Escolhendo a Estrutura Certa
OperaçãoArrayListLinkedListHashSetTreeSetHashMapAcessoO(1) ✅O(n)——O(1) ✅Inserção (início)O(n)O(1) ✅———Inserção (fim)O(1) ✅O(1) ✅O(1) ✅O(log n)O(1) ✅RemoçãoO(n)O(n)O(1) ✅O(log n)O(1) ✅BuscaO(n)O(n)O(1) ✅O(log n)O(1) ✅Ordenado❌❌❌✅❌
Mostrar Imagem
Regra de Ouro: Escolha a estrutura baseado em suas operações mais frequentes.
9. Referências
- Collections Documentation: docs.oracle.com/javase/tutorial/collections
- Java Collections Cheat Sheet: cheatsheets.sitepoint.com
- Big-O Complexity: bigocheatsheet.com
Conclusão
Collections não são um detalhe técnico — são o alicerce de aplicações eficientes. Conhecer as diferenças entre ArrayList e LinkedList, entre HashSet e TreeSet, entre HashMap e ConcurrentHashMap faz toda a diferença quando você precisa escalar suas aplicações.
Use a estrutura certa para o trabalho certo, e suas aplicações Java rodarão com performance e elegância.
Chamada para Ação
👉 Código : Acessar o cÓdigo pratico com o uso de collections e suas aplicações -- > CLIQUE AQUI
👉 Desafio: Identifique Collections em seu código atual e verifique se são as mais eficientes para seus casos de uso. Refatore e meça a melhoria de performance!
📌 Próximo nível: Explore Generics avançados e Collections com padrões de design.