Set e Map em Java
Estruturas avançadas para dados únicos e mapeamentos chave-valor
1. O que são Set e Map?
Set é uma coleção que não permite duplicatas. Perfeita para manter valores únicos.
Map é uma estrutura de chave-valor. Cada chave é única e mapeia um valor.
Ambas são extremamente úteis em cenários reais onde precisa buscar rapidamente ou garantir unicidade.
2. Set - Conjunto de Valores Únicos
Características:
- Sem duplicatas - valores únicos
- Sem ordem garantida (em HashSet)
- Busca rápida - O(1)
- Não indexado - sem get(i)
Implementações Principais:
HashSet
Melhor performance. Sem ordem. Usa hash table internamente.
TreeSet
Mantém ordem ordenada. Performance O(log n). Usa árvore interna.
LinkedHashSet
Mantém ordem de inserção. Performance entre HashSet e TreeSet.
import java.util.*;
// HashSet - sem ordem
Set cores = new HashSet<>();
cores.add("Vermelho");
cores.add("Azul");
cores.add("Vermelho"); // Não adiciona (duplicata)
System.out.println("Total: " + cores.size()); // 2
// TreeSet - ordem ordenada
Set numeros = new TreeSet<>();
numeros.add(5);
numeros.add(1);
numeros.add(3);
System.out.println(numeros); // [1, 3, 5]
// LinkedHashSet - ordem de inserção
Set nomes = new LinkedHashSet<>();
nomes.add("João");
nomes.add("Maria");
nomes.add("Pedro");
// Iteração mantém ordem: João, Maria, Pedro
3. Operações com Set
Set frutas = new HashSet<>();
frutas.add("Maçã");
frutas.add("Banana");
frutas.add("Laranja");
// Verificar presença
if (frutas.contains("Maçã")) {
System.out.println("Tem maçã!");
}
// Tamanho
System.out.println("Total: " + frutas.size()); // 3
// Remover
frutas.remove("Banana");
// Iterar
for (String fruta : frutas) {
System.out.println(fruta);
}
// Limpar
frutas.clear();
System.out.println(frutas.isEmpty()); // true
// Operações de conjunto
Set numeros1 = new HashSet<>(Arrays.asList("1", "2", "3"));
Set numeros2 = new HashSet<>(Arrays.asList("2", "3", "4"));
// União
Set uniao = new HashSet<>(numeros1);
uniao.addAll(numeros2);
System.out.println("União: " + uniao); // [1, 2, 3, 4]
// Interseção
Set intersecao = new HashSet<>(numeros1);
intersecao.retainAll(numeros2);
System.out.println("Interseção: " + intersecao); // [2, 3]
// Diferença
Set diferenca = new HashSet<>(numeros1);
diferenca.removeAll(numeros2);
System.out.println("Diferença: " + diferenca); // [1]
4. Map - Estrutura Chave-Valor
Características:
- Chaves únicas - não permite duplicatas de chave
- Busca por chave - O(1) em HashMap
- Valores podem repetir - mas chaves não
- Mapeamento 1:1 - cada chave mapeia um valor
Implementações Principais:
HashMap
Melhor performance. Sem ordem. Usa hash table. Permite null.
TreeMap
Chaves ordenadas. O(log n). Red-black tree internamente.
LinkedHashMap
Mantém ordem de inserção. Performance similar a HashMap.
import java.util.*;
// HashMap - melhor performance
Map populacao = new HashMap<>();
populacao.put("Brasil", 215000000);
populacao.put("Japão", 125000000);
populacao.put("Alemanha", 84000000);
// Buscar por chave
int pop = populacao.get("Brasil");
System.out.println("População Brasil: " + pop);
// TreeMap - chaves ordenadas
Map salarios = new TreeMap<>();
salarios.put("Zara", 3000);
salarios.put("Ana", 2500);
salarios.put("Bruno", 2800);
// Iteração: Ana, Bruno, Zara (ordem alfabética)
// LinkedHashMap - ordem de inserção
Map contatos = new LinkedHashMap<>();
contatos.put("João", "11-999999");
contatos.put("Maria", "21-888888");
// Iteração: João, Maria (ordem de inserção)
5. Operações com Map
Map notas = new HashMap<>();
notas.put("João", 85);
notas.put("Maria", 92);
notas.put("Pedro", 78);
// PUT - adicionar ou atualizar
notas.put("João", 90); // Atualiza
// GET - obter valor
int nota = notas.get("Maria"); // 92
// GETORDEFAULT - com padrão
int notaDesconhecida = notas.getOrDefault("Carlos", 0); // 0
// CONTAINSKEY - verifica chave
if (notas.containsKey("Pedro")) {
System.out.println("Pedro tem nota: " + notas.get("Pedro"));
}
// CONTAINSVALUE - verifica valor
if (notas.containsValue(92)) {
System.out.println("Alguém tirou 92!");
}
// SIZE - quantidade
System.out.println("Total de alunos: " + notas.size());
// REMOVE - remover
notas.remove("Pedro");
// CLEAR - limpar tudo
// notas.clear();
// ITERAR - chaves
for (String aluno : notas.keySet()) {
System.out.println(aluno + ": " + notas.get(aluno));
}
// ITERAR - valores
for (Integer n : notas.values()) {
System.out.println("Nota: " + n);
}
// ITERAR - pares (Entry)
for (Map.Entry entry : notas.entrySet()) {
System.out.println(entry.getKey() + " -> " + entry.getValue());
}
6. Comparação Set vs Map vs List
| Aspecto | List | Set | Map |
|---|---|---|---|
| Ordem | Mantém ordem | Sem ordem (depende) | Sem ordem (depende) |
| Duplicatas | Permite | Não permite | Chave sim, valor não |
| Acesso | Por índice | Por iteração | Por chave |
| Performance | O(1) acesso | O(1) busca | O(1) busca |
| Uso | Sequências | Valores únicos | Mapeamentos |
7. Exemplo Prático: Sistema de Votação
class SistemaVotacao {
private Set votantesJaVotaram; // Set de identidades únicas
private Map votos; // Mapa candidato -> quantidade
public SistemaVotacao() {
this.votantesJaVotaram = new HashSet<>();
this.votos = new HashMap<>();
}
// Verificar se já votou
public boolean jaVotou(String cpf) {
return votantesJaVotaram.contains(cpf);
}
// Registrar voto
public void votar(String cpf, String candidato) {
if (jaVotou(cpf)) {
System.out.println("Você já votou!");
return;
}
votantesJaVotaram.add(cpf);
// Atualizar contagem
votos.put(candidato, votos.getOrDefault(candidato, 0) + 1);
System.out.println("Voto registrado para: " + candidato);
}
// Apuração
public void apurar() {
System.out.println("=== APURAÇÃO ===");
// Encontrar vencedor
String vencedor = "";
int maiorVoto = 0;
for (Map.Entry entry : votos.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue() + " votos");
if (entry.getValue() > maiorVoto) {
maiorVoto = entry.getValue();
vencedor = entry.getKey();
}
}
System.out.println("\nVENCEDOR: " + vencedor + " (" + maiorVoto + " votos)");
System.out.println("Total de votantes: " + votantesJaVotaram.size());
}
}
// USO
SistemaVotacao sistema = new SistemaVotacao();
sistema.votar("12345678", "Candidato A");
sistema.votar("87654321", "Candidato B");
sistema.votar("12345678", "Candidato C"); // Já votou!
sistema.votar("11111111", "Candidato A");
sistema.apurar();
8. HashSet vs TreeSet vs LinkedHashSet
// Mesma entrada
int[] dados = {3, 1, 4, 1, 5, 9, 2, 6};
// HashSet - sem ordem
Set hash = new HashSet<>(Arrays.asList(3, 1, 4, 5, 9, 2, 6));
System.out.println("HashSet: " + hash);
// Saída: [1, 2, 3, 4, 5, 6, 9] ou qualquer ordem
// TreeSet - ordenado
Set tree = new TreeSet<>(Arrays.asList(3, 1, 4, 5, 9, 2, 6));
System.out.println("TreeSet: " + tree);
// Saída: [1, 2, 3, 4, 5, 6, 9]
// LinkedHashSet - ordem de inserção
Set linked = new LinkedHashSet<>(Arrays.asList(3, 1, 4, 5, 9, 2, 6));
System.out.println("LinkedHashSet: " + linked);
// Saída: [3, 1, 4, 5, 9, 2, 6]
// Performance
long inicio = System.nanoTime();
Set grande = new HashSet<>();
for (int i = 0; i < 1000000; i++) {
grande.add(i);
}
long tempo = System.nanoTime() - inicio;
System.out.println("HashSet 1M: " + (tempo / 1000000.0) + "ms");
// TreeSet é mais lento
inicio = System.nanoTime();
Set grandeTree = new TreeSet<>();
for (int i = 0; i < 1000000; i++) {
grandeTree.add(i);
}
tempo = System.nanoTime() - inicio;
System.out.println("TreeSet 1M: " + (tempo / 1000000.0) + "ms"); // Mais lento
9. HashMap vs TreeMap vs LinkedHashMap
// HashMap - sem ordem
Map hash = new HashMap<>();
hash.put("Zara", 3000);
hash.put("Ana", 2500);
hash.put("Bruno", 2800);
System.out.println("HashMap: " + hash);
// Saída: qualquer ordem
// TreeMap - chaves ordenadas
Map tree = new TreeMap<>();
tree.put("Zara", 3000);
tree.put("Ana", 2500);
tree.put("Bruno", 2800);
System.out.println("TreeMap: " + tree);
// Saída: {Ana=2500, Bruno=2800, Zara=3000}
// LinkedHashMap - ordem de inserção
Map linked = new LinkedHashMap<>();
linked.put("Zara", 3000);
linked.put("Ana", 2500);
linked.put("Bruno", 2800);
System.out.println("LinkedHashMap: " + linked);
// Saída: {Zara=3000, Ana=2500, Bruno=2800}
// Quando usar
// HashMap - quando quer máximo performance
// TreeMap - quando precisa de ordem alfabética/numérica
// LinkedHashMap - quando precisa de ordem de inserção
10. Boas Práticas com Set e Map
✓ FAÇA:
- Use HashSet/HashMap para performance máxima
- Use TreeSet/TreeMap quando precisa de ordem
- Use contains() antes de adicionar em Set
- Itere com entrySet() em Map, não com keySet() + get()
- Use getOrDefault() para valores opcionais
✗ EVITE:
- Não use Set/Map sem entender a diferença
- Não itere com keySet() + get() em Map (use entrySet)
- Não modifique Set enquanto itera
- Não use objetos mutáveis como chave em Map
- Não compare performance sem know da aplicação
11. Exemplo Complexo: Análise de Dados
import java.util.*;
class AnalisadorPalavras {
private Map frequencia;
private Set palavrasUnicas;
public AnalisadorPalavras(String texto) {
this.frequencia = new HashMap<>();
this.palavrasUnicas = new HashSet<>();
String[] palavras = texto.toLowerCase()
.split("\\W+");
for (String palavra : palavras) {
if (!palavra.isEmpty()) {
palavrasUnicas.add(palavra);
frequencia.put(palavra,
frequencia.getOrDefault(palavra, 0) + 1);
}
}
}
public void exibirEstatisticas() {
System.out.println("=== ANÁLISE ===");
System.out.println("Palavras únicas: " + palavrasUnicas.size());
System.out.println("Total de palavras: " +
frequencia.values().stream().mapToInt(Integer::intValue).sum());
System.out.println("\nFrequência:");
frequencia.entrySet().stream()
.sorted((a, b) -> b.getValue().compareTo(a.getValue()))
.forEach(e -> System.out.println(
e.getKey() + ": " + e.getValue()
));
}
}
// USO
String texto = "Java é legal. Java é poderoso. Java é utilizado.";
AnalisadorPalavras analise = new AnalisadorPalavras(texto);
analise.exibirEstatisticas();
12. Exercícios Práticos
Exercício 1: Remover Duplicatas
Use HashSet para remover duplicatas de uma lista.
Exercício 2: Contagem de Frequência
Use HashMap para contar quantas vezes cada elemento aparece.
Exercício 3: Validador de Email
Use Set para armazenar emails registrados e validar novos.
Exercício 4: Agenda Telefônica
Implemente com Map para armazenar nome -> telefone.
Exercício 5: Ranking Ordenado
Use TreeMap para manter pontuações em ordem decrescente.
Conclusão
Set e Map são estruturas poderosas para dados únicos e mapeamentos.
Choose corretamente: HashMap para performance, TreeMap para ordem, LinkedHashMap para sequência.
Domine essas estruturas e seu código Java será mais eficiente e elegante! 🚀