Interface Set e Classe HashSet em Java
A interface Set
, que faz parte do conjunto de Coleções do Java, estende a interface Collection
para definir um contrato para coleções que não permitem elementos duplicados. Ela não introduz novos métodos, mas adapta o comportamento dos métodos herdados para garantir a unicidade. Por exemplo, o método add()
só insere um elemento se ele ainda não estiver presente na coleção, retornando true
em caso de sucesso e false
caso contrário.
A Classe HashSet: Implementação Baseada em Tabela de Hash
A classe genérica HashSet<E>
é uma das implementações mais comuns e eficientes da interface Set
. Ela utiliza uma tabela de hash como estrutura de dados interna para armazenar seus elementos.
Uma tabela de hash é uma estrutura que associa cada objeto a uma chave única, chamada de código hash. Esse código permite que o HashSet
localize, insira ou remova elementos com alta performance (em tempo médio constante, O(1)), pois não necessita percorrer a coleção. A principal consequência dessa abordagem é que a HashSet
não garante a ordem de iteração dos elementos.
Construtores da Classe HashSet
Para instanciar um HashSet
, pode-se utilizar um dos seguintes construtores:
HashSet()
: Cria um conjunto vazio com capacidade inicial e fator de carga padrão.HashSet(Collection<? extends E> col)
: Cria um conjunto contendo todos os elementos da coleçãocol
, eliminando quaisquer duplicatas presentes na coleção de origem.HashSet(int capacity)
: Cria um conjunto vazio com a capacidade inicial especificada. A capacidade padrão é 16.HashSet(int capacity, float loadFactor)
: Cria um conjunto com a capacidade inicial e o fator de carga (loadFactor
) especificados. O fator de carga, um valor entre 0.0 e 1.0, determina o quão cheia a tabela pode ficar antes de sua capacidade ser automaticamente aumentada.
Exemplo de Utilização
O código a seguir demonstra as operações fundamentais de um HashSet
, incluindo a adição de elementos, a tentativa de inserir duplicatas e o trabalho com objetos personalizados.
import java.util.HashSet;
public class Program {
public static void main(String[] args) {
// Criação de um HashSet para armazenar Strings
HashSet<String> states = new HashSet<String>();
// Adição de elementos ao conjunto
states.add("Germany");
states.add("France");
states.add("Italy");
// Tentativa de adicionar um elemento que já existe na coleção
boolean isAdded = states.add("Germany");
// O método retorna false, pois o elemento não foi adicionado
System.out.println("'Germany' was added again: " + isAdded);
// O tamanho permanece 3, pois duplicatas são ignoradas
System.out.printf("Set contains %d elements\n", states.size());
// Iteração sobre os elementos (a ordem não é garantida)
System.out.println("Elements in the set:");
for(String state : states) {
System.out.println(state);
}
// Remoção de um elemento
states.remove("Germany");
System.out.println("Set after removing 'Germany': " + states);
// Utilização de um HashSet com objetos personalizados
HashSet<Person> people = new HashSet<Person>();
people.add(new Person("Mike"));
people.add(new Person("Tom"));
people.add(new Person("Nick"));
System.out.println("People in the set:");
for(Person p : people) {
System.out.println(p.getName());
}
}
}
class Person {
private String name;
public Person(String value) {
this.name = value;
}
public String getName() {
return this.name;
}
}
Nota importante: Para que a detecção de duplicatas funcione corretamente com objetos personalizados (como a classe
Person
), é fundamental que a classe sobrescreva os métodosequals(Object o)
ehashCode()
. Sem isso, oHashSet
não será capaz de identificar dois objetosPerson
com o mesmo nome como sendo duplicados.
Resumo
- A interface
Set
define uma coleção que armazena elementos únicos. - A classe
HashSet
é uma implementação deSet
que utiliza uma tabela de hash, oferecendo alta performance para as operações de adição, remoção e busca. - A principal característica do
HashSet
é a garantia de unicidade dos elementos, em detrimento da manutenção da ordem de inserção. - Use
Set
eHashSet
quando seu principal requisito for garantir que não haja elementos repetidos em uma coleção e a ordem não for um fator importante.
📝 Exercícios
Tarefa 1 (Teoria)
Descrição: Considere o trecho de código abaixo. Após a execução de todas as linhas, qual será o tamanho final do conjunto (numeros.size()
) e por quê?
import java.util.HashSet;
import java.util.Set;
public class TesteSet {
public static void main(String[] args) {
Set<Integer> numeros = new HashSet<>();
numeros.add(10);
numeros.add(20);
numeros.add(30);
numeros.add(10); // Adicionando um valor duplicado
numeros.add(20); // Adicionando outro valor duplicado
System.out.println("Tamanho do conjunto: " + numeros.size());
}
}
Alternativas:
- O tamanho será 5, pois cinco operações
add()
foram chamadas. - O código lançará uma exceção ao tentar adicionar um elemento duplicado.
- O tamanho será 3, pois a interface
Set
não permite elementos duplicados. - O tamanho será 2, pois apenas os elementos duplicados são mantidos.
- O código não irá compilar.
Resposta
Set
não permite elementos duplicados.
Set
, incluindo o HashSet
, é a garantia de que ela conterá apenas elementos únicos.
add()
inserem os valores 10
, 20
e 30
com sucesso.numeros.add(10);
novamente, o HashSet
verifica que o valor 10
já existe na coleção e, portanto, a operação é ignorada (o método add()
retornaria false
).20
novamente.
Como resultado, o conjunto conterá apenas os elementos {10, 20, 30}
, e seu tamanho final será 3.Tarefa 2 (Prática)
Descrição: Você recebeu uma List
de nomes que pode conter duplicatas. Sua tarefa é criar um método que receba essa lista e retorne um HashSet
contendo apenas os nomes únicos.
Código inicial:
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> nomesComDuplicatas = new ArrayList<>();
nomesComDuplicatas.add("Ana");
nomesComDuplicatas.add("Carlos");
nomesComDuplicatas.add("Ana");
nomesComDuplicatas.add("Beatriz");
nomesComDuplicatas.add("Carlos");
HashSet<String> nomesUnicos = obterNomesUnicos(nomesComDuplicatas);
System.out.println("Nomes únicos: " + nomesUnicos);
// A ordem pode variar, mas a saída deve conter Ana, Carlos, Beatriz.
}
public static HashSet<String> obterNomesUnicos(List<String> nomes) {
// Crie um HashSet a partir da lista de nomes para remover as duplicatas.
// Dica: um dos construtores de HashSet é perfeito para isso.
}
}
Resposta
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> nomesComDuplicatas = new ArrayList<>();
nomesComDuplicatas.add("Ana");
nomesComDuplicatas.add("Carlos");
nomesComDuplicatas.add("Ana");
nomesComDuplicatas.add("Beatriz");
nomesComDuplicatas.add("Carlos");
HashSet<String> nomesUnicos = obterNomesUnicos(nomesComDuplicatas);
System.out.println("Nomes únicos: " + nomesUnicos);
}
public static HashSet<String> obterNomesUnicos(List<String> nomes) {
// O construtor de HashSet que recebe uma coleção já faz o trabalho
// de iterar sobre os elementos e adicionar apenas os únicos.
return new HashSet<>(nomes);
}
}
HashSet
. O construtor HashSet(Collection c)
recebe qualquer tipo de coleção (como uma ArrayList
) e, ao criar o novo HashSet
, ele itera sobre todos os elementos da coleção de origem. Para cada elemento, ele tenta adicioná-lo ao novo conjunto. Como o Set
não permite duplicatas, apenas a primeira ocorrência de cada nome ("Ana", "Carlos", "Beatriz") é mantida, resultando em um conjunto com elementos únicos.