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:
LinkedHashSetpreserva a ordem de inserção, a ordem em que você adicionou os elementos.TreeSetmantém os elementos ordenados pela ordem natural (ou por umComparatorque 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.