Menu

Java HashSet: valores únicos, pertencimento e operações de conjuntos

Como usar o HashSet do Java para coleções de valores únicos: add, contains, remover, eliminar duplicatas de uma lista e combinar conjuntos com união, interseção e diferença.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

Um conjunto armazena valores únicos

Um HashSet (de java.util) é uma coleção que guarda cada valor no máximo uma vez. Não há chaves nem valores emparelhados como em um HashMap, apenas um saco de elementos distintos. Sua única função é responder rápido a uma pergunta: "isso está aqui dentro?"

Há um único parâmetro de tipo, <ElementType>. Assim como com ArrayList e HashMap, normalmente você declara a variável com o tipo da interface Set e constrói um HashSet.

add retorna se o valor era novo

add não apenas armazena o valor: ele retorna um boolean informando se o conjunto realmente mudou. Adicionar um valor que já está presente retorna false e deixa o conjunto intacto.

Esse valor de retorno é genuinamente útil: if (!seen.add(x)) { /* x é uma repetição */ } permite detectar duplicatas em uma única linha à medida que você avança.

Removendo duplicatas de uma lista

Como um conjunto rejeita repetições, a forma mais rápida de eliminar duplicatas de uma coleção é despejá-la em um. O construtor de HashSet aceita qualquer outra coleção:

Esse é o motivo mais comum pelo qual iniciantes recorrem a um conjunto. Só saiba que você perde a ordem original no caminho de ida e volta, use LinkedHashSet se a ordem importar (visto abaixo).

contains, remove e size

As operações do dia a dia espelham as das outras coleções:

A grande vantagem sobre um ArrayList é o contains. Uma lista precisa percorrer todos os elementos para respondê-lo (O(n)); um HashSet chega quase direto à resposta (aproximadamente O(1)). Quando você se pegar chamando list.contains(...) dentro de um laço, esse costuma ser o sinal para mudar para um conjunto.

Operações de conjuntos: união, interseção, diferença

Os conjuntos se destacam quando você os combina. Os métodos se leem quase como linguagem natural quando você sabe qual é qual:

O detalhe crucial: addAll, retainAll e removeAll modificam o conjunto sobre o qual são chamados. É por isso que cada exemplo copia a para um HashSet novo primeiro, caso contrário você destruiria o original. Crie um conjunto novo para cada resultado.

HashSet não mantém a ordem

Assim como HashMap, um HashSet não promete nenhuma ordem de iteração, e a ordem pode variar entre execuções. Se você precisa de previsibilidade:

  • LinkedHashSet preserva a ordem de inserção, a ordem em que você adicionou os elementos.
  • TreeSet mantém os elementos ordenados pela ordem natural (ou por um Comparator que você fornecer).

Os três implementam a interface Set, então alternar entre eles é uma mudança de uma única linha no construtor.

Os elementos precisam ser hasheáveis

Um HashSet é apoiado internamente por um HashMap, então vale a mesma regra: ele localiza os elementos calculando o hash deles, o que significa que o hashCode() e o equals() de um elemento precisam ser consistentes. Tipos nativos como String e Integer já fazem isso corretamente, por isso as strings "java" duplicadas se fundem corretamente acima. Se você armazenar instâncias da sua própria classe, sobrescreva tanto equals quanto hashCode; caso contrário, dois objetos que são "iguais" em significado serão tratados como distintos, e contains e a eliminação de duplicatas falharão silenciosamente.

Próximo: Iterando coleções

Você já conheceu as três coleções fundamentais: ArrayList, HashMap e HashSet. Cada uma é percorrida de um jeito um pouco diferente, e há armadilhas sutis (como modificar uma coleção enquanto itera sobre ela). A seguir vamos juntar tudo e ver como iterar coleções de forma limpa com o laço for-each, os iteradores e o forEach.

Perguntas frequentes

Como criar um HashSet em Java?

Declare-o com um único parâmetro de tipo (o tipo do elemento) e chame o construtor: Set<String> tags = new HashSet<>();. Adicione valores com tags.add("java"); e teste o pertencimento com tags.contains("java");. Importe java.util.HashSet e java.util.Set.

Qual é a diferença entre um HashSet e um ArrayList em Java?

Um ArrayList mantém todos os elementos que você adiciona (inclusive duplicatas) na ordem de inserção e é acessado por posição. Um HashSet armazena apenas valores únicos, não promete nenhuma ordem, não tem índice e sua verificação contains tem tempo aproximadamente constante em vez de percorrer a lista inteira. Use um HashSet quando o que importa é a unicidade ou a verificação rápida de pertencimento, não a posição.

Como remover duplicatas de uma lista em Java?

Passe a lista para o construtor de um HashSet: Set<String> unique = new HashSet<>(list);. O conjunto descarta os valores repetidos automaticamente. Se você precisar de uma lista de volta (e não se importar em perder a ordem), envolva-o novamente: new ArrayList<>(unique). Use um LinkedHashSet se quiser preservar a ordem original.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR