Obtendo enum associado ao valor int

88

Anteriormente, eu tinha minhas enums LegNo definidas simplesmente como:

NO_LEG, LEG_ONE, LEG_TWO

e, ao ligar return LegNo.values()[i];, consegui obter o valor associado a cada enum.

Mas agora eu decidi que quero que o LegNoenum NO_LEGseja o int -1 em vez de 0, então decidi usar um construtor privado para inicializar e definir seu valor int

NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

private LegNo(final int leg) { legNo = leg; }

a única coisa agora é que, como estou fazendo dessa maneira, o values()método não funcionará para o NO_LEGenum. Como faço para obter o enum associado ao int? Existe alguma maneira eficiente de fazer isso além de usar uma instrução case switch ou if-elseif-elseif

Eu posso ver um monte de perguntas SO relacionadas com a obtenção do valor int do enum, mas estou atrás do contrário.

L-Samuels
fonte

Respostas:

146

EDITAR agosto de 2018

Hoje eu implementaria isso da seguinte maneira

public enum LegNo {
    NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

    private final int value;

    LegNo(int value) {
        this.value = value;
    }

    public static Optional<LegNo> valueOf(int value) {
        return Arrays.stream(values())
            .filter(legNo -> legNo.value == value)
            .findFirst();
    }
}

Você terá que manter um mapeamento dentro do enum.

public enum LegNo {
    NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

    private int legNo;

    private static Map<Integer, LegNo> map = new HashMap<Integer, LegNo>();

    static {
        for (LegNo legEnum : LegNo.values()) {
            map.put(legEnum.legNo, legEnum);
        }
    }

    private LegNo(final int leg) { legNo = leg; }

    public static LegNo valueOf(int legNo) {
        return map.get(legNo);
    }
}

O bloco estático será chamado apenas uma vez, portanto, não há praticamente nenhum problema de desempenho aqui.

EDIT: renomeado o método para valueOf, pois é mais alinhado com outras classes Java.

Adarshr
fonte
desculpe, eu não tenho certeza se fui claro o suficiente. quero passar no int e obter o enum associado a ele.
L-Samuels de
@ L-Samuels Acho que não li sua pergunta corretamente. Veja minha atualização.
adarshr de
2
Eu sei que isso parece óbvio, mas usá-lo assim: LegNo foo = LegNo.valueOf(2);. O código anterior retornará a LegNo.LEG_TWO.
FirstOne
1
Para ser observado, a passagem de um valor inteiro inválido (não mapeado) retornará null, conforme esperado usando HashMap.get : Retorna o valor para o qual a chave especificada está mapeada, ou nulo se este mapa não contém mapeamento para a chave.
FirstOne
Embora a sintaxe do stream seja limpa, vale ressaltar que ela tem uma complexidade de tempo maior do que o mapa estático (que, reconhecidamente, tem um consumo de memória maior). Não é um problema para 3 valores, mas definitivamente uma preocupação se você estiver valueOf()usando um enum de 1000 membros dentro de outro loop.
Patrick M
24

Você também pode incluir um método estático no enum que itera por meio de todos os membros e retorna o correto.

public enum LegNo {
   NO_LEG(-1),
   LEG_ONE(1),
   LEG_TWO(2);

   private int legIndex;

   private LegNo(int legIndex) { this.legIndex = legIndex; }

   public static LegNo getLeg(int legIndex) {
      for (LegNo l : LegNo.values()) {
          if (l.legIndex == legIndex) return l;
      }
      throw new IllegalArgumentException("Leg not found. Amputated?");
   }
}

Agora, se você quiser obter um valor Enum pelo inteiro, basta usar:

int myLegIndex = 1; //expected : LEG_ONE
LegNo myLeg = LegNo.getLeg(myLegIndex);
Mike Adler
fonte
Suponho que isso seria mais elegante do que usar uma instrução if else if. Mas, se houvesse mais enums para pesquisar, a estratégia de mapa sugerida por @adarshr seria melhor. Embora vote a favor do humor.
L-Samuels de
1
Também gosto muito da estratégia de mapas. Especialmente quando o enum tem muitos valores ou precisa ser pesquisado por meio desse mecanismo com frequência. No entanto, se pesquisar valores pelo int associado for uma ocorrência relativamente rara ou se você tiver muitos enums diferentes com o mesmo requisito de pesquisa, acredito que meu caminho seria mais amigável com os recursos, já que a sobrecarga para o mapa é salva. Além disso, acho que torna o código menos confuso. No entanto, tenho alguns casos de uso em que definitivamente mudarei para o tipo Mapa.
Mike Adler de
Você nunca deve derivar um valor de enum associado por seu ordinal. Usar um mapa estático é a metodologia recomendada por arquitetos Java.
hfontanez
O campo legIndex coincide com o ordinal neste exemplo, mas pode ser qualquer valor int. Nenhuma pesquisa ordinal é executada. Além disso, forneça ou crie um link para o motivo pelo qual você acha que a pesquisa ordinal é ruim.
Mike Adler
1
"Perna não encontrada. Amputada?"
Gnagy
17

Resposta de adarshr adaptada para Java 8:

import static java.util.Arrays.stream;
import static java.util.stream.Collectors.toMap;

import java.util.Map;

public enum LegNo {
    NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

    private final int legNo;

    private final static Map<Integer, LegNo> map =
            stream(LegNo.values()).collect(toMap(leg -> leg.legNo, leg -> leg));

    private LegNo(final int leg) {
        legNo = leg;
    }

    public static LegNo valueOf(int legNo) {
        return map.get(legNo);
    }
}
Marcin
fonte
11

Você também pode acessar o valor Enum correspondente ao valor inteiro fornecido simplesmente chamando o método values ​​() no enum LegNo. Ele retorna o campo de enums LegNo: LegNo.values()[0]; //returns LEG_NO LegNo.values()[1]; //returns LEG_ONE LegNo.values()[2]; //returns LEG_TWO

Não exatamente o que ele estava procurando, mas bem parecido e muito simples. (Embora o assunto esteja morto, pode ser útil para outra pessoa.)

Tadeas
fonte
6

Java 8 way com valor padrão:

public enum LegNo {
    NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

    private final int legNo;

    LegNo(int legNo) {
        this.legNo = legNo;
    }

    public static LegNo find(int legNo, Supplier<? extends LegNo> byDef) {
        return Arrays.asList(LegNo.values()).stream()
                .filter(e -> e.legNo == legNo).findFirst().orElseGet(byDef);
    }
}

chamar:

LegNo res = LegNo.find(0, () -> LegNo.NO_LEG);

ou com exceção:

LegNo res = LegNo.find(0, () -> {
    throw new RuntimeException("No found");
});
Dmitry Sokolyuk
fonte
1

Como seu enum contém apenas 3 elementos, a maneira mais rápida será usar apenas uma série de if else, como você sugeriu.

editar: a resposta que adarshr forneceu é mais adequada para casos gerais, onde há muitos valores enum, mas acho que é um exagero para o seu problema.

DieterDP
fonte
Ter um Mapem seu código certamente não é um exagero. Além disso, torna o método muito mais limpo do que um espaguete de condições if-else.
adarshr de
Eu concordo que o Map é melhor quando você tem muitos valores enum, mas para 3 valores eu ficaria com uma construção if / else. É uma questão de gosto, eu acho.
DieterDP de
Qualquer que seja a abordagem escolhida, a assinatura do método de public LegNo valueOf(int value)não deve ser alterada. O if-else poderia então ser escrito dentro do próprio enum. Se o if-else sair do enum, ele certamente se tornará um código não tão limpo.
adarshr de
1
Eu concordo totalmente com você :)
DieterDP
1
public enum LegNo {
    NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

    private int legNo;

    private LegNo(int leg) { legNo = leg; }

    public static LegNo valueOf(int legNo) {
        for (LegNo leg : LegNo.values()) {
            if (leg.legNo == legNo) return leg;
        }   
    }
}

assert LegNo.valueOf(2) == LegNo.LEG_TWO
assert LegNo.valueOf(3) == null
Tom B
fonte
4
Aceitável para enums com <10 valores, mas totalmente ineficaz para um grande número de valores de enum devido à complexidade da pesquisa O (n)
Alfishe
1
public enum LegNo {

  NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

  private final int code;

  LegNo(int code) {
    this.code = code;
    ReverseStorage.reverseMap.put(code, this);
  }

  public static Optional<LegNo> getByCode(int code) {
    return Optional.ofNullable(ReverseStorage.reverseMap.get(code));
  }

  private static final class ReverseStorage {
    private static final Map<Integer, LegNo> reverseMap = new LinkedHashMap<>();
  }
}
Andrey Lebedenko
fonte