W3docs

Interface Set do Java

Coleções de elementos únicos em Java com a interface Set e suas principais implementações.

Set<E> é uma Collection<E> com uma regra adicional: sem duplicatas. A interface em si não adiciona quase nenhum método novo — ela herda add, remove, contains, size, iterator e os demais. O que ela muda é o que esses métodos prometem: add(e) retorna false (e não altera nada) se um elemento igual já estiver no conjunto; dois conjuntos são equals se contiverem os mesmos elementos independentemente da ordem; e hashCode é a soma dos hashes dos elementos, para que conjuntos iguais sempre concordem.

Esse pequeno contrato — "elementos são únicos" — é exatamente o que você precisa para testes de pertinência, deduplicação, interseção/união e mil outros padrões que ficam feios com uma List.

O que é considerado duplicata

Um Set decide a duplicação por equals (e, em conjuntos baseados em hash, hashCode como função de bucket). Não é igualdade por referência, nem "parece igual quando impresso." Se você colocar sua própria classe em um Set, deve sobrescrever ambos os métodos juntos; o capítulo equals e hashCode cobriu o contrato.

Set<String> tags = new HashSet<>();
tags.add("java");
tags.add("java");      // add returns false; the set still has one element
tags.add(new String("java")); // also false — equals, not reference

Uma armadilha comum: armazenar elementos mutáveis e depois alterá-los enquanto estão em um conjunto baseado em hash. O hash do elemento muda, o conjunto o busca no bucket errado e contains começa a mentir. A regra: se você colocar um objeto em um hash set, trate-o como efetivamente imutável a partir desse ponto. O TreeSet tem a mesma armadilha com compareTo.

As quatro implementações padrão

O java.util oferece quatro implementações de Set que cobrem quase todos os casos de uso:

ClasseEstrutura de suporteOrdenaçãoElementos nullUso típico
HashSettabela hashnenhumaum null permitidoo padrão — testes de pertinência mais rápidos
LinkedHashSettabela hash + lista duplamente encadeadaordem de inserçãoum null permitidoquando a ordem de iteração deve corresponder à ordem de inserção
TreeSetárvore rubro-negraordem natural / comparatorsem nullquando você precisa de iteração ordenada ou consultas por intervalo
EnumSetvetor de bitsordem de declaração do enumsem nullum Set<MyEnum> — compacto, rápido e ordenado

As três primeiras são cobertas nos três próximos capítulos; EnumSet é coberto mais adiante no livro, junto com EnumMap.

As operações em massa: união, interseção, diferença

Um Set herda quatro operações em massa de Collection, e em um Set elas adquirem os significados da teoria dos conjuntos:

  • addAll(other)união (no lugar). Após a chamada, a contém todos os elementos de ambos os lados.
  • retainAll(other)interseção (no lugar). Após a chamada, a contém apenas os elementos que também estavam em other.
  • removeAll(other)diferença (no lugar). Após a chamada, a contém apenas os elementos que não estavam em other.
  • containsAll(other)teste de subconjunto. Retorna true se todo elemento de other estiver em a.

Essas operações modificam o receptor. Se precisar de uma versão não destrutiva, copie primeiro: Set<E> u = new HashSet<>(a); u.addAll(b);.

Igualdade e códigos hash de conjuntos

O contrato de equals do Set é incomum: dois conjuntos são iguais se contiverem os mesmos elementos, independentemente da ordem ou do tipo de implementação. Um HashSet, um LinkedHashSet e um TreeSet com os mesmos elementos são todos iguais entre si.

Set<Integer> a = new HashSet<>(List.of(1, 2, 3));
Set<Integer> b = new TreeSet<>(List.of(3, 2, 1));
System.out.println(a.equals(b));   // true

É por isso que as operações em massa e os métodos de fábrica imutáveis podem transitar livremente entre implementações — apenas a regra "mesmos elementos" é observada.

Fábricas somente leitura e imutáveis

Desde o Java 9, a maneira mais fácil de criar um conjunto pequeno e fixo é Set.of(...):

Set<String> primary = Set.of("red", "green", "blue");

Set.of retorna um conjunto imutável que rejeita elementos null e lança exceção se você passar uma duplicata na construção. Para um snapshot defensivo de um conjunto existente, use Set.copyOf(existing) — também imutável, também rejeita nulls.

Para uma visão que oculta mutações (o original ainda pode ser modificado, mas os chamadores não podem adicionar através da visão), use Collections.unmodifiableSet(s). O capítulo sobre coleções não modificáveis, mais adiante nesta parte, cobre quando escolher cada opção.

Um exemplo prático: deduplicação, álgebra de conjuntos e ordem

O programa abaixo usa todas as quatro implementações, demonstra as operações em massa com dados reais e mostra como a ordem de iteração difere entre HashSet, LinkedHashSet e TreeSet.

java— editable, runs on the server

O que observar na execução:

  • add retornou false para toda duplicata \"java\" — é assim que você escreve um deduplicador em duas linhas.
  • União, interseção e diferença são cada uma um addAll/retainAll/removeAll — copie primeiro se não quiser perder o original.
  • As três implementações contêm os mesmos elementos e são iguais entre si, mas iteram em ordens muito diferentes. Escolha a implementação pela ordem que você precisa, não por reflexo.

O que vem a seguir

A implementação de Set mais comum — e a que você escolhe a menos que tenha um motivo para não usar — é baseada em tabela hash. O HashSet é o próximo; vamos cobrir fator de carga, o rehash e o que "tempo quase constante" significa na prática.

Prática

Prática
O que `set.add(x)` retorna quando `x` já é um elemento de `set`, de acordo com o contrato de `Set`?
O que `set.add(x)` retorna quando `x` já é um elemento de `set`, de acordo com o contrato de `Set`?
Was this page helpful?