Set e HashSet

Set é uma coleção sem duplicatas. Esta lição cobre o uso de Set e princípios de desduplicação.

Características da Interface Set

Característica Descrição
Não ordenada Sem ordem fixa (HashSet)
Sem duplicatas Desduplicação automática
Sem índice Não pode acessar por índice

HashSet

HashSet é baseado em HashMap e é a implementação de Set mais comumente usada.

Criando HashSet

JAVA
import java.util.HashSet;
import java.util.Set;

Set<String> set1 = new HashSet<>();
Set<String> set2 = new HashSet<>(100);  // Capacidade especificada
Set<String> set3 = new HashSet<>(set1);  // De outra coleção

Operações Básicas

JAVA
import java.util.HashSet;
import java.util.Set;

public class HashSetDemo {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        
        // Adicionar elementos
        set.add("Alice");
        set.add("Bob");
        set.add("Charlie");
        set.add("Alice");  // Duplicata, não será adicionado
        System.out.println("Set: " + set);  // [Alice, Bob, Charlie] (não ordenado)
        
        // Verificar
        System.out.println("Tamanho: " + set.size());  // 3
        System.out.println("Contém Alice: " + set.contains("Alice"));  // true
        System.out.println("Está vazio: " + set.isEmpty());  // false
        
        // Remover
        set.remove("Bob");
        System.out.println("Após remover: " + set);
        
        // Percorrer
        for (String name : set) {
            System.out.println(name);
        }
    }
}

Princípio de Desduplicação

HashSet usa hashCode e equals para determinar se elementos são duplicatas.

Fluxo de Desduplicação

TEXT
1. Calcular hashCode do elemento
2. Se hashCode diferente → não é duplicata
3. Se hashCode igual → chamar equals
4. equals retorna true → duplicata
5. equals retorna false → não é duplicata (colisão de hash)

Exemplo: Desduplicação de Objeto Personalizado

JAVA
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

public class Student {
    private String name;
    private int age;
    
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    // Deve sobrescrever hashCode e equals
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age && Objects.equals(name, student.name);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
    
    @Override
    public String toString() {
        return name + "(" + age + ")";
    }
    
    public static void main(String[] args) {
        Set<Student> set = new HashSet<>();
        set.add(new Student("Alice", 20));
        set.add(new Student("Bob", 22));
        set.add(new Student("Alice", 20));  // Duplicata, não será adicionado
        
        System.out.println("Tamanho: " + set.size());  // 2
        System.out.println("Set: " + set);
    }
}
▶ Experimente
⚠️ Importante: Ao armazenar objetos personalizados no HashSet, você deve sobrescrever os métodos hashCode e equals, caso contrário a desduplicação não funcionará corretamente.

TreeSet

TreeSet é baseado em árvore rubro-negra e os elementos são automaticamente ordenados.

Características

Característica Descrição
Ordenada Elementos ordenados por ordem natural ou personalizada
Sem duplicatas Desduplicação automática
Sem índice Não pode acessar por índice

Exemplo: TreeSet

JAVA
import java.util.Set;
import java.util.TreeSet;

public class TreeSetDemo {
    public static void main(String[] args) {
        // Ordem natural
        Set<Integer> numbers = new TreeSet<>();
        numbers.add(3);
        numbers.add(1);
        numbers.add(4);
        numbers.add(1);  // Duplicata, não será adicionado
        numbers.add(5);
        System.out.println("Conjunto ordenado: " + numbers);  // [1, 3, 4, 5]
        
        // Ordem de string
        Set<String> names = new TreeSet<>();
        names.add("Charlie");
        names.add("Alice");
        names.add("Bob");
        System.out.println("Nomes ordenados: " + names);  // [Alice, Bob, Charlie]
    }
}
▶ Experimente

Ordem Personalizada

JAVA
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;

public class CustomSortDemo {
    public static void main(String[] args) {
        // Ordenar por comprimento
        Set<String> words = new TreeSet<>(Comparator.comparingInt(String::length));
        words.add("Hello");
        words.add("Hi");
        words.add("Java");
        words.add("Go");
        System.out.println("Por comprimento: " + words);  // [Go, Hi, Java, Hello]
    }
}

LinkedHashSet

LinkedHashSet mantém a ordem de inserção.

Características

Característica Descrição
Ordenada Na ordem de inserção
Sem duplicatas Desduplicação automática

Exemplo: LinkedHashSet

JAVA
import java.util.LinkedHashSet;
import java.util.Set;

public class LinkedHashSetDemo {
    public static void main(String[] args) {
        Set<String> set = new LinkedHashSet<>();
        set.add("Charlie");
        set.add("Alice");
        set.add("Bob");
        System.out.println("Conjunto ordenado: " + set);  // [Charlie, Alice, Bob]
    }
}
▶ Experimente

Operações de Conjunto

União

JAVA
Set<Integer> set1 = new HashSet<>(Arrays.asList(1, 2, 3));
Set<Integer> set2 = new HashSet<>(Arrays.asList(3, 4, 5));

Set<Integer> union = new HashSet<>(set1);
union.addAll(set2);
System.out.println("União: " + union);  // [1, 2, 3, 4, 5]

Interseção

JAVA
Set<Integer> intersection = new HashSet<>(set1);
intersection.retainAll(set2);
System.out.println("Interseção: " + intersection);  // [3]

Diferença

JAVA
Set<Integer> diff = new HashSet<>(set1);
diff.removeAll(set2);
System.out.println("Diferença: " + diff);  // [1, 2]

Comparação de Set

Característica HashSet TreeSet LinkedHashSet
Ordem Não ordenada Ordenada Ordem de inserção
Implementação HashMap Árvore rubro-negra Lista ligada + HashMap
Performance O(1) O(log n) O(1)
null 1 permitido Não permitido 1 permitido

Guia de Seleção

Necessidade Escolha
Não se importa com ordem HashSet
Precisa de ordenação TreeSet
Manter ordem de inserção LinkedHashSet

Exemplo: Prática de Desduplicação

JAVA
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class RemoveDuplicates {
    // Método 1: Desduplicação com Set
    public static <T> List<T> removeDuplicates1(List<T> list) {
        return new ArrayList<>(new LinkedHashSet<>(list));
    }
    
    // Método 2: Desduplicação com Stream
    public static <T> List<T> removeDuplicates2(List<T> list) {
        return list.stream().distinct().collect(Collectors.toList());
    }
    
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("Alice");
        list.add("Bob");
        list.add("Alice");
        list.add("Charlie");
        list.add("Bob");
        
        System.out.println("Original: " + list);
        System.out.println("Desduplicado: " + removeDuplicates1(list));
    }
}

❓ Perguntas Frequentes

P: Como escolher entre Set e List? R: Use Set para desduplicação. Use List quando precisar de elementos ordenados e duplicados.

P: Qual é a regra de ordenação do TreeSet? R: O padrão é ordenação natural (Comparable). Você pode personalizar com Comparator.

P: Por que o HashSet é rápido? R: Baseado em HashMap, usa hashCode para busca direta com complexidade de tempo O(1).

📖 Resumo

📝 Exercícios

  1. Desduplicação: Remova duplicatas de uma List mantendo a ordem original
  2. Interseção: Encontre a interseção de dois Sets
  3. Ordenação: Use TreeSet para ordenar strings por comprimento

Próxima Lição

Na próxima lição, aprenderemos sobre Map e HashMap — coleções de pares chave-valor.

100%