Получение enum, связанного со значением int

89

Раньше мои перечисления LegNo определялись просто как:

NO_LEG, LEG_ONE, LEG_TWO

и позвонив return LegNo.values()[i];, я смог получить значение, связанное с каждым перечислением.

Но теперь я решил, что хочу, чтобы LegNoперечисление NO_LEGбыло int -1 вместо 0, поэтому я решил использовать частный конструктор для инициализации и установки его значения int.

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

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

единственное, что сейчас это то, что, поскольку я делаю это таким образом, values()метод не будет работать для NO_LEGперечисления. Как мне получить перечисление, связанное с int? Есть ли какой-либо эффективный способ сделать это, кроме использования оператора case switch или if-elseif-elseif

Я вижу много SO-вопросов, связанных с получением значения int из перечисления, но я ищу обратное.

L-Samuels
источник

Ответы:

148

ИЗМЕНИТЬ август 2018

Сегодня я бы реализовал это следующим образом

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();
    }
}

Вам нужно будет поддерживать отображение внутри перечисления.

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);
    }
}

Статический блок будет вызван только один раз, поэтому здесь практически нет проблем с производительностью.

РЕДАКТИРОВАТЬ: переименовал метод в, так valueOfкак он больше соответствует другим классам Java.

Адаршр
источник
извините, я не уверен, достаточно ли ясно. Я хочу передать int и получить связанное с ним перечисление.
L-Samuels
@ L-Samuels Думаю, я неправильно прочитал ваш вопрос. Смотрите мое обновление.
adarshr
2
Я знаю , это кажется очевидным, но использовать это нравится так: LegNo foo = LegNo.valueOf(2);. Предыдущий код вернет LegNo.LEG_TWO.
FirstOne
1
Следует отметить, что передача недопустимого целочисленного значения (не сопоставленного) вернет null, как и ожидалось, с помощью HashMap.get : возвращает значение, которому сопоставлен указанный ключ, или null, если эта карта не содержит сопоставления для ключа.
FirstOne
Хотя синтаксис потока аккуратный, стоит отметить, что он имеет более высокую временную сложность, чем статическая карта (которая, по общему признанию, потребляет больше памяти). Это не проблема для трех значений, но определенно вызывает беспокойство, если вы valueOf()используете перечисление из 1000 членов внутри другого цикла.
Patrick M
24

Вы также можете включить в перечисление статический метод, который выполняет итерацию по всем членам и возвращает правильный.

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?");
   }
}

Теперь, если вы хотите получить значение Enum целым числом, вы просто используете:

int myLegIndex = 1; //expected : LEG_ONE
LegNo myLeg = LegNo.getLeg(myLegIndex);
Майк Адлер
источник
Я полагаю, это было бы более элегантно, чем использование оператора if else if. Но, учитывая, что для поиска было больше перечислений, стратегия карты, предложенная @adarshr, была бы лучше. Хотя за юмор проголосую.
L-Samuels
1
Мне тоже очень нравится стратегия карты. Особенно, когда перечисление имеет много значений или его нужно очень часто искать с помощью этого механизма. Однако, если поиск значений по связанному int является относительно редким явлением или у вас есть много разных перечислений с одним и тем же требованием поиска, я считаю, что мой способ будет более дружественным к ресурсам, поскольку накладные расходы для карты сохраняются. Кроме того, я считаю, что это делает код менее загроможденным. Тем не менее, у меня есть несколько вариантов использования, в которых я обязательно перейду на тип карты.
Майк Адлер,
Никогда не следует выводить связанное значение перечисления по порядковому номеру. Использование статической карты ЯВЛЯЕТСЯ рекомендуемой методологией архитекторов Java.
hfontanez 02
Поле legIndex совпадает с порядковым номером в этом примере, но может иметь любое значение типа int. Порядковый поиск не выполняется. Кроме того, укажите или укажите причину, по которой вы считаете, что порядковый поиск - это плохо.
Майк Адлер
1
«Нога не найдена. Ампутирована?»
Gnagy
17

Ответ adarshr адаптирован к 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);
    }
}
Марчин
источник
11

Вы также можете получить доступ к значению Enum, соответствующему заданному целочисленному значению, просто вызвав метод values ​​() для enum LegNo. Возвращает поле перечислений LegNo: LegNo.values()[0]; //returns LEG_NO LegNo.values()[1]; //returns LEG_ONE LegNo.values()[2]; //returns LEG_TWO

Не совсем то, что он искал, но довольно близко и действительно очень просто. (Хотя объект мертв, он может быть полезен кому-то другому.)

Тадеас
источник
6

Java 8 способ со значением по умолчанию:

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);
    }
}

звонить:

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

или с исключением:

LegNo res = LegNo.find(0, () -> {
    throw new RuntimeException("No found");
});
Дмитрий Соколюк
источник
2
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<>();
  }
}
Андрей Лебеденко
источник
1

Поскольку ваше перечисление содержит только 3 элемента, самым быстрым способом будет просто использовать серию if else, как вы предложили.

edit: ответ, который предоставил adarshr, лучше подходит для общих случаев, когда есть много значений перечисления, но я думаю, что это излишек для вашей проблемы.

DieterDP
источник
Наличие Mapв вашем коде, конечно, не излишество. Кроме того, это делает метод намного чище, чем набор условий if-else.
adarshr
Я согласен с тем, что карта лучше, если у вас много значений перечисления, но для трех значений я бы придерживался конструкции if / else. Думаю, это дело вкуса.
DieterDP
Какой бы подход мы ни выбрали, подпись метода public LegNo valueOf(int value)не должна изменяться. Тогда if-else можно было бы записать в самом перечислении. Если if-else выходит из перечисления, то это, безусловно, становится не очень чистым кодом.
adarshr
1
Я полностью с вами согласен :)
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
Том Б
источник
4
Приемлемо для перечислений с <10 ​​значений, но совершенно неэффективно для большого количества значений перечислений из-за сложности поиска O (n)
Alfishe