Преобразование из enum-ординала в тип перечисления

У меня есть тип перечисления ReportTypeEnum, который передается между методами во всех моих классах, но затем мне нужно передать это по URL-адресу, поэтому я использую порядковый метод для получения значения int. После того, как я получу его на другой странице JSP, мне нужно преобразовать его обратно в ReportTypeEnum, чтобы я мог продолжить его передачу.

Как я могу преобразовать порядковый номер в ReportTypeEnum?

Использование Java 6 SE.

Ответ 1

Чтобы преобразовать порядковый номер в его перечисление enum, вы можете сделать это:

ReportTypeEnum value = ReportTypeEnum.values()[ordinal];

Обратите внимание на границы массива.

Обратите внимание, что каждый вызов values() возвращает вновь клонированный массив, который может негативно повлиять на производительность. Вы можете захотеть кэшировать массив, если он будет вызываться часто.

Пример кода о кешировании values().


Этот ответ был отредактирован для включения обратной связи, указанной внутри комментариев

Ответ 2

Это почти наверняка плохая идея. Конечно, если ординал де-факто сохраняется (например, потому, что кто-то заклалвил URL-адрес) - это означает, что вы всегда должны сохранять заказ enum в будущем, что может быть неясно, чтобы скомпилировать сопровождающих по строке.

Почему бы не закодировать enum с помощью myEnumValue.name() (и декодировать через ReportTypeEnum.valueOf(s)) вместо?

Ответ 3

Если я собираюсь использовать values() много:

enum Suit {
   Hearts, Diamonds, Spades, Clubs;
   public static final Suit values[] = values();
}

Между тем wherever.java:

Suit suit = Suit.values[ordinal];

Обратите внимание на границы массива.

Ответ 4

Вы можете использовать статическую таблицу поиска:

public enum Suit {
  spades, hearts, diamonds, clubs;

  private static final Map<Integer, Suit> lookup = new HashMap<Integer, Suit>();

  static{
    int ordinal = 0;
    for (Suit suit : EnumSet.allOf(Suit.class)) {
      lookup.put(ordinal, suit);
      ordinal+= 1;
    }
  }

  public Suit fromOrdinal(int ordinal) {
    return lookup.get(ordinal);
  }
}

Ответ 5

Я согласен с большинством людей, что использование ординала - это, вероятно, плохая идея. Обычно я решаю эту проблему, предоставляя enum частному конструктору, который может взять, например, значение DB, затем создать статическую функцию fromDbValue, аналогичную той, что была в ответе Jan.

public ReportTypeEnum {
    R1(1),
    R2(2),
    R3(3),
    R4(4),
    R5(5),
    R6(6),
    R7(7),
    R8(8);

    private static Logger log = LoggerFactory.getLogger(ReportEnumType.class);  
    private static Map<Integer, ReportTypeEnum> lookup;
    private Integer dbValue;

    private ReportTypeEnum(Integer dbValue) {
        this.dbValue = dbValue;
    }


    static {
        try {
            ReportTypeEnum[] vals = ReportTypeEnum.values();
            lookup = new HashMap<Integer, ReportTypeEnum>(vals.length);

            for (ReportTypeEnum  rpt: vals)
                lookup.put(rpt.getDbValue(), rpt);
         }
         catch (Exception e) {
             // Careful, if any exception is thrown out of a static block, the class
             // won't be initialized
             log.error("Unexpected exception initializing " + ReportTypeEnum.class, e);
         }
    }

    public static ReportTypeEnum fromDbValue(Integer dbValue) {
        return lookup.get(dbValue);
    }

    public Integer getDbValue() {
        return this.dbValue;
    }

}

Теперь вы можете изменить порядок без изменения поиска и наоборот.

Ответ 6

Это то, что я использую. Я не претендую на то, что он гораздо менее эффективен, чем более простые решения, описанные выше. Что он делает, так это предоставление более четкого сообщения об исключении, чем "ArrayIndexOutOfBounds", когда в вышеприведенном решении используется недопустимое порядковое значение.

Он использует тот факт, что enumSet javadoc указывает, что итератор возвращает элементы в своем естественном порядке. Там утверждают, если это неверно.

Тест JUnit4 демонстрирует, как он используется.

 /**
 * convert ordinal to Enum
 * @param clzz may not be null
 * @param ordinal
 * @return e with e.ordinal( ) == ordinal
 * @throws IllegalArgumentException if ordinal out of range
 */
public static <E extends Enum<E> > E lookupEnum(Class<E> clzz, int ordinal) {
    EnumSet<E> set = EnumSet.allOf(clzz);
    if (ordinal < set.size()) {
        Iterator<E> iter = set.iterator();
        for (int i = 0; i < ordinal; i++) {
            iter.next();
        }
        E rval = iter.next();
        assert(rval.ordinal() == ordinal);
        return rval;
    }
    throw new IllegalArgumentException("Invalid value " + ordinal + " for " + clzz.getName( ) + ", must be < " + set.size());
}

@Test
public void lookupTest( ) {
    java.util.concurrent.TimeUnit tu = lookupEnum(TimeUnit.class, 3);
    System.out.println(tu);
}

Ответ 7

Это то, что я делаю на Android с Proguard:

public enum SomeStatus {
    UNINITIALIZED, STATUS_1, RESERVED_1, STATUS_2, RESERVED_2, STATUS_3;//do not change order

    private static SomeStatus[] values = null;
    public static SomeStatus fromInteger(int i) {
        if(SomeStatus.values == null) {
            SomeStatus.values = SomeStatus.values();
        }
        if (i < 0) return SomeStatus.values[0];
        if (i >= SomeStatus.values.length) return SomeStatus.values[0];
        return SomeStatus.values[i];
    }
}

это коротко, и мне не нужно беспокоиться о том, что в Proguard есть исключение

Ответ 8

public enum Suit implements java.io.Serializable, Comparable<Suit>{
  spades, hearts, diamonds, clubs;
  private static final Suit [] lookup  = Suit.values();
  public Suit fromOrdinal(int ordinal) {
    if(ordinal< 1 || ordinal> 3) return null;
    return lookup[value-1];
  }
}

тестовый класс

public class MainTest {
    public static void main(String[] args) {
        Suit d3 = Suit.diamonds;
        Suit d3Test = Suit.fromOrdinal(2);
        if(d3.equals(d3Test)){
            System.out.println("Susses");
        }else System.out.println("Fails");
    }
}

Я ценю, что вы делитесь с нами, если у вас более эффективный код. Мое перечисление огромно и постоянно называется тысячи раз.

Ответ 9

Предположим, у вас есть следующее перечисление:

public enum ErrorCodes {
    BUSINESS_ERROR(100), SERVER_ERROR(500), NETWORK_ERROR(1000);
}

По умолчанию вы не сможете получить экземпляр ErrorCode по его числовому значению, поскольку java не предоставляет никакого способа получить экземпляр Enum с помощью поля.

Чтобы это сделать, я обычно выставляю статический хэш файл внутри класса Enum, который сопоставляет каждое значение с соответствующим экземпляром Enum следующим образом:

public enum ErrorCodes {
    BUSINESS_ERROR(100), SERVER_ERROR(500), NETWORK_ERROR(1000);

    private int errorCode;
    private static Map<Integer, ErrorCodes> errorCodeByErrorNumber = new HashMap<Integer, ErrorCodes>();

    static {
        for (ErrorCodes errorCode : ErrorCodes.values()) {
            errorCodeByErrorNumber.put(errorCode.getErrorCode(), errorCode);
        }
    }

    private ErrorCodes(int errorCode) {
        this.errorCode = errorCode;
    }

    public int getErrorCode() {
        return errorCode;
    }

    public static ErrorCodes getErrorCodeByNumber(Integer errorNumber) {
        return errorCodeByErrorNumber.get(errorNumber);
    }
}

Для более общих операций Enum, проверьте следующее: Как использовать Enums в java

Ответ 10

Таким образом, один из способов - сделать ExampleEnum valueOfOrdinal = ExampleEnum.values()[ordinal]; который работает и его легко, однако, как упоминалось ранее, ExampleEnum.values() возвращает новый клонированный массив для каждого вызова. Это может быть излишне дорого. Мы можем решить это, ExampleEnum[] values = values() массив следующим образом: ExampleEnum[] values = values(). Также "опасно" разрешать изменение нашего кэшированного массива. Кто-то может написать ExampleEnum.values[0] = ExampleEnum.type2; Так что я бы сделал его закрытым с помощью метода доступа, который не выполняет дополнительное копирование.

private enum ExampleEnum{
    type0, type1, type2, type3;
    private static final ExampleEnum[] values = values();
    public static ExampleEnum value(int ord) {
        return values[ord];
    }
}

Вы должны использовать ExampleEnum.value(ordinal) чтобы получить значение enum, связанное с ordinal

Ответ 11

Вы можете определить простой метод, например:

public enum Alphabet{
    A,B,C,D;

    public static Alphabet get(int index){
        return Alphabet.values()[index];
    }
}

И используйте это как:

System.out.println(Alphabet.get(2));

Ответ 12

Каждое перечисление имеет имя(), которое дает строку с именем члена перечисления.

Учитывая enum Suit{Heart, Spade, Club, Diamond}, Suit.Heart.name() даст Heart.

Каждое перечисление имеет метод valueOf(), который принимает тип перечисления и строку для выполнения обратной операции:

Enum.valueOf(Suit.class, "Heart") возвращает Suit.Heart.

Почему кто-то будет использовать ординалы вне меня. Это может быть на наносекунды быстрее, но это небезопасно, если члены перечисления изменяются, поскольку другой разработчик может не знать, что какой-то код использует порядковые значения (особенно на странице JSP, указанной в вопросе, сетевые и базовые служебные данные полностью доминируют над время, не используя целое число над строкой).