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 |
|---|---|---|---|---|---|
|
|
máscara de bits | O(1) | muito pouco | não |
|
|
tabela hash | O(1) | mais | sim |
|
|
array por ordinal | O(1) | muito pouco | não |
|
|
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.
GO TO FULL VERSION