CodeGym /Cursos /JAVA 25 SELF /EnumSet/EnumMap

EnumSet/EnumMap

JAVA 25 SELF
Nível 28 , Lição 3
Disponível

1. Introdução

Em Java existem coleções especiais para trabalhar com enumerações (enum): EnumSet e EnumMap. Elas fazem parte da biblioteca padrão (java.util) e são projetadas para trabalhar da forma mais eficiente possível com tipos enum.

Como o EnumSet e o EnumMap funcionam por dentro?

EnumSet funciona como uma máscara de bits. Imagine um conjunto de flags, em que cada elemento da enumeração ocupa exatamente um bit. Se o bit estiver ligado — o elemento está no conjunto; se estiver desligado — ele não está. Tudo é armazenado em um array de números (long[]), e se o seu enum tiver menos de 64 valores, então todo o conjunto cabe em um único número!

EnumMap é ainda mais simples: é um array de valores em que o índice é o número ordinal (ordinal) do elemento do enum. Em vez de um HashMap<Enum, V> convencional, você obtém uma estrutura muito compacta e rápida.

O que isso proporciona? As operações de adição, remoção e verificação são O(1). O uso de memória é mínimo (especialmente em comparação com HashSet e HashMap). A iteração dos elementos sempre segue a ordem de declaração no enum, o que torna o resultado previsível.

Exemplo: como isso fica

enum Day { MON, TUE, WED, THU, FRI, SAT, SUN }

EnumSet<Day> weekend = EnumSet.of(Day.SAT, Day.SUN);
System.out.println(weekend); // [SAT, SUN]

Por fora, é um conjunto comum. Mas por dentro — um número com dois bits ligados: um para SAT e outro para SUN. Se você adicionar FRI, mais um bit será ligado. Nada de tabela hash nem objetos extras.

EnumMap<Day, String> schedule = new EnumMap<>(Day.class);
schedule.put(Day.MON, "Gym");
schedule.put(Day.FRI, "Party");
System.out.println(schedule); // {MON=Gym, FRI=Party}

Aqui as chaves (Day) são transformadas em índices internos de array; por isso, o acesso funciona tão rápido quanto acessar um elemento de um array.

2. Casos de uso: flags, tabelas, autômatos finitos

EnumSet: ideal para flags e conjuntos de estados

  • Flags: armazenamento de conjuntos “ligado/desligado” para uma quantidade limitada de opções.
  • Conjunto de valores do enum: dias da semana, permissões de usuário, estados de tarefas.
  • Autômatos finitos (FSM): é prático manter transições permitidas em um EnumSet.

Exemplo: flags de acesso

enum Permission { READ, WRITE, EXECUTE }

EnumSet<Permission> perms = EnumSet.of(Permission.READ, Permission.WRITE);
if (perms.contains(Permission.WRITE)) {
    // Escrita permitida
}

Exemplo: todos os valores, exceto alguns

EnumSet<Day> workdays = EnumSet.complementOf(EnumSet.of(Day.SAT, Day.SUN));
System.out.println(workdays); // [MON, TUE, WED, THU, FRI]

EnumMap: ideal para tabelas com chaves enum

  • Tabelas de correspondência: as chaves são valores do enum e os valores — quaisquer objetos.
  • Acesso rápido: mais rápido e compacto do que HashMap<Enum, V>.

Exemplo: preços por dia da semana

EnumMap<Day, Integer> prices = new EnumMap<>(Day.class);
prices.put(Day.MON, 100);
prices.put(Day.SAT, 200);
System.out.println(prices.get(Day.SAT)); // 200

Exemplo: autômato finito

enum State { START, RUNNING, STOPPED }
EnumMap<State, EnumSet<State>> transitions = new EnumMap<>(State.class);
transitions.put(State.START, EnumSet.of(State.RUNNING));
transitions.put(State.RUNNING, EnumSet.of(State.STOPPED));
transitions.put(State.STOPPED, EnumSet.noneOf(State.class));

3. Armadilhas: alterações no enum e serialização

EnumSet e EnumMap dependem da composição e da ordem dos valores do seu enum. Se você adicionar um novo elemento, remover um antigo ou trocá-los de lugar, coleções salvas ou serializadas podem se comportar incorretamente.

Serialização

  • EnumSet e EnumMap são serializáveis, mas se o enum mudar entre a serialização e a desserialização, podem ocorrer erros/perda de dados.
  • Remover um valor do enum após a serialização quase certamente levará a uma exceção na leitura.

Boa prática:

  • Não serialize EnumSet/EnumMap se você não tem certeza de que o enum é estável.
  • Para armazenamento de longo prazo, use, por exemplo, uma lista das representações em string dos valores.

4. Nuances úteis

Comparação entre EnumSet/EnumMap e coleções comuns

Coleção Chave/elemento Implementação interna Desempenho Memória null
EnumSet
enum
máscara de bits O(1) muito pouco não
HashSet<Enum>
enum
tabela hash O(1) mais sim
EnumMap
enum
array por ordinal O(1) muito pouco não
HashMap<Enum, V>
enum
tabela hash O(1) mais sim

Boas práticas

  • Use EnumSet para conjuntos de valores (of, noneOf, allOf, complementOf).
  • Use EnumMap para tabelas associativas em que a chave é um enum.
  • Evite enumerações “gigantescas” — listas com centenas de valores prejudicam a compacidade.
  • Não serialize se o enum puder mudar.
  • Não use null como chave ou valor.

5. Prática: como usar EnumSet e EnumMap na aplicação

Exemplo: armazenamento de papéis do usuário

enum Role { USER, ADMIN, MODERATOR }

class User {
    private EnumSet<Role> roles = EnumSet.noneOf(Role.class);

    public void addRole(Role role) {
        roles.add(role);
    }

    public boolean isAdmin() {
        return roles.contains(Role.ADMIN);
    }
}

Exemplo: tabela de transições de estados

enum State { NEW, IN_PROGRESS, DONE }

EnumMap<State, EnumSet<State>> transitions = new EnumMap<>(State.class);
transitions.put(State.NEW, EnumSet.of(State.IN_PROGRESS));
transitions.put(State.IN_PROGRESS, EnumSet.of(State.DONE));
transitions.put(State.DONE, EnumSet.noneOf(State.class));

6. Erros comuns ao trabalhar com EnumSet/EnumMap

Erro nº 1: uso de EnumSet/EnumMap com tipo que não é enum.
Essas coleções funcionam apenas com tipos que são enum.

// EnumSet<String> set = EnumSet.of("A", "B"); // Erro de compilação!

Erro nº 2: uso de EnumSet/EnumMap para enums muito grandes.
Enums com centenas de valores pioram a compacidade. Ainda assim, isso geralmente continua melhor em memória do que HashSet/HashMap para os mesmos dados.

Erro nº 3: alterar o enum após a serialização.
Adicionar/remover/reordenar valores após a serialização leva a erros na leitura ou à perda de dados.

Erro nº 4: uso de EnumSet/EnumMap com null.
Nem os elementos de EnumSet, nem as chaves/valores de EnumMap podem ser null.

EnumSet<Day> days = EnumSet.of(null); // NullPointerException!
EnumMap<Day, String> map = new EnumMap<>(Day.class);
map.put(null, "test"); // NullPointerException!

Erro nº 5: esperar que EnumSet seja um “Set comum”.
EnumSet armazena apenas valores do seu enum; você não pode adicionar um objeto arbitrário fora da enumeração.

Erro nº 6: uso de EnumSet/EnumMap para enums “mutáveis”.
Se as enumerações forem geradas ou substituídas em tempo de execução (carregamento dinâmico/reflexão), essas estruturas não funcionarão corretamente.

Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION