JAVA Learning

Hub de Conteúdo

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.

java
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

java
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.

java
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

java
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

java
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

java
// 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

java
// 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

java
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! 🚀