Перетворити цілочисельне значення на відповідне Java Enum


86

У мене така перерахування:

public enum PcapLinkType {
  DLT_NULL(0)
  DLT_EN10MB(1)
  DLT_EN3MB(2),
  DLT_AX25(3),
  /*snip, 200 more enums, not always consecutive.*/
  DLT_UNKNOWN(-1);
    private final int value;   

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

Тепер я отримую int із зовнішнього вводу і хочу відповідний вхід - викид винятку, якщо значення не існує, це нормально, але бажано, щоб він був DLT_UNKNOWN у такому випадку.

int val = in.readInt();
PcapLinkType type = ???; /*convert val to a PcapLinkType */

Відповіді:


105

Вам потрібно буде зробити це вручну, додавши в клас статичну карту, яка відображає цілі числа до перечислень, наприклад

private static final Map<Integer, PcapLinkType> intToTypeMap = new HashMap<Integer, PcapLinkType>();
static {
    for (PcapLinkType type : PcapLinkType.values()) {
        intToTypeMap.put(type.value, type);
    }
}

public static PcapLinkType fromInt(int i) {
    PcapLinkType type = intToTypeMap.get(Integer.valueOf(i));
    if (type == null) 
        return PcapLinkType.DLT_UNKNOWN;
    return type;
}

1
оновлено рекомендаціями dty, що було непоганою ідеєю.
MeBigFatGuy

Сподіваюся, ви спочатку пропустили мій код через компілятор ... Я щойно вигадав це з голови. Я знаю, що техніка працює - я використовував її вчора. Але код на іншій машині, і у нього немає моїх інструментів розробника.
dty

1
allOf доступний лише для наборів
MeBigFatGuy

1
Крім того, EnumMapвикористовує перелічення як ключі. У цьому випадку OP хоче перерахувати як значення.
п'ятниця,

8
Здається, це багато непотрібних накладних витрат. Ті, хто насправді потребує цього типу операції, мабуть, потребують високої продуктивності, оскільки вони пишуть / читають з потоків / сокетів, і в цьому випадку кешування values()(якщо значення перелічувань є послідовними) або просте switchвисловлювання зможе перемогти цей метод . Якщо у вас є лише кілька записів, Enumтоді немає сенсу додавати накладні витрати на HashMap просто для зручності, щоб не потрібно оновлювати switchоператор. Цей метод може здатися більш елегантним, але він також марнотратний.
розчавити

30

Там є статичний метод , values()який є документально, але не там , де ви очікуєте: http://docs.oracle.com/javase/tutorial/java/javaOO/enum.html

enum MyEnum {
    FIRST, SECOND, THIRD;
    private static MyEnum[] allValues = values();
    public static MyEnum fromOrdinal(int n) {return allValues[n];}
}

В принципі, ви можете використовувати просто values()[i], але ходять чутки, які values()створюватимуть копію масиву кожного разу, коли він буде викликаний.


9
За Джошуа Блохом (Ефективна книга про Java) : Ніколи не виводьте значення, пов’язане з переліченням, з його порядкового номера; Ваша реалізація не повинна покладатися на порядок перерахувань.
stevo.mit

4
Впровадження чого? Якщо ми реалізуємо якийсь алгоритм, реалізація не повинна покладатися на порядок перерахувань, якщо цей порядок не задокументований. Коли ми реалізуємо сам перелік, добре використовувати такі деталі реалізації так само, як добре використовувати приватні методи класу.
18446744073709551615

1
Не погоджуйтесь. Я вважаю, ніколи не мається на увазі незалежно від документації. Ви не повинні використовувати порядкові знаки, навіть коли ви самостійно виконуєте перерахування. Це неприємний запах і схильний до помилок. Я не фахівець, але я б не сперечався з Джошуа Блохом :)
stevo.mit

4
@ stevo.mit подивіться на новий перелік enva java.time.Month у Java 8. Статичний метод Month.of (int) робить саме те, що Джошуа Блох сказав, що ви ніколи не повинні робити. Він повертає Місяць за своїм порядковим номером.
Klitos Kyriacou

1
@ stevo.mit Є впорядковані перерахування та невпорядковані перелічення . (І перелічення бітових масок теж.) Просто некоректно говорити про них як про просто "перелічення". Рішення, якими виражальними засобами користуватися, повинно базуватися на рівні абстракції, над яким ви працюєте. Дійсно неправильно використовувати деталі реалізації (виразні засоби з нижчого рівня) або припущення щодо використання (виразні засоби з вищого рівня). Що стосується " ніколи ", то в людських мовах ніколи ніколи не означає ніколи, тому що завжди існує певний контекст. (Зазвичай, у програмуванні додатків, ніколи ...) BTW, programering.com/a/MzNxQjMwATM.html
18446744073709551615

14

Вам доведеться створити новий статичний метод, де ви повторите PcapLinkType.values ​​() і порівняєте:

public static PcapLinkType forCode(int code) {
    for (PcapLinkType typе : PcapLinkType.values()) {
        if (type.getValue() == code) {
            return type;
        }
    }
    return null;
 }

Це було б непогано, якщо його називають рідко. Якщо його часто викликають, тоді подивіться на Mapоптимізацію, запропоновану іншими.


4
Може бути дорогим, якщо телефонувати багато. Побудова статичної карти, швидше за все, принесе кращу амортизовану вартість.
dty

@dty o (n) з n = 200 - я не думаю, що це проблема
Божо

7
Це абсолютно смішне твердження без відчуття того, як часто це називають. Якщо це зателефонували один раз, добре. Якщо це вимагається для кожного пакету, який проскакує в мережі 10Ge, то зробити алгоритм в 200 разів швидшим дуже важливо. Отже, чому я кваліфікував свою заяву як "якщо називають багато"
dty

10

Ви можете зробити щось подібне, щоб автоматично зареєструвати їх усі у колекції, за допомогою якої потім легко перетворити цілі числа у відповідний перелік. (До речі, додавати їх на карту в конструкторі перерахування заборонено . Приємно вивчати нові речі навіть після багатьох років використання Java. :)

public enum PcapLinkType {
    DLT_NULL(0),
    DLT_EN10MB(1),
    DLT_EN3MB(2),
    DLT_AX25(3),
    /*snip, 200 more enums, not always consecutive.*/
    DLT_UNKNOWN(-1);

    private static final Map<Integer, PcapLinkType> typesByValue = new HashMap<Integer, PcapLinkType>();

    static {
        for (PcapLinkType type : PcapLinkType.values()) {
            typesByValue.put(type.value, type);
        }
    }

    private final int value;

    private PcapLinkType(int value) {
        this.value = value;
    }

    public static PcapLinkType forValue(int value) {
        return typesByValue.get(value);
    }
}

1
Ось що ви отримуєте за подвійну перевірку своєї відповіді перед публікацією. ;)
Еско Луонтола 13.03.11

10

якщо у вас такий перелік

public enum PcapLinkType {
  DLT_NULL(0)
  DLT_EN10MB(1)
  DLT_EN3MB(2),
  DLT_AX25(3),
  DLT_UNKNOWN(-1);

    private final int value;   

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

тоді ви можете використовувати це як

PcapLinkType type = PcapLinkType.values()[1]; /*convert val to a PcapLinkType */

ви пропустили коментар / * snip, ще 200 перерахувань, не завжди поспіль. * /
MeBigFatGuy

про всяк випадок, якщо значення вашого переліку - це перехідність від Нуля, це погана практика
cuasodayleo

4

Як каже @MeBigFatGuy, окрім того, що ви можете змусити ваш static {...}блок використовувати цикл над values()колекцією:

static {
    for (PcapLinkType type : PcapLinkType.values()) {
        intToTypeMap.put(type.getValue(), type);
    }
}

4

Я знаю, що цьому питанню кілька років, але оскільки Java 8 нас тим часом принесла Optional, я подумав запропонувати рішення, використовуючи його (та Streamі Collectors):

public enum PcapLinkType {
  DLT_NULL(0),
  DLT_EN3MB(2),
  DLT_AX25(3),
  /*snip, 200 more enums, not always consecutive.*/
  // DLT_UNKNOWN(-1); // <--- NO LONGER NEEDED

  private final int value;
  private PcapLinkType(int value) { this.value = value; }

  private static final Map<Integer, PcapLinkType> map;
  static {
    map = Arrays.stream(values())
        .collect(Collectors.toMap(e -> e.value, e -> e));
  }

  public static Optional<PcapLinkType> fromInt(int value) {
    return Optional.ofNullable(map.get(value));
  }
}

Optionalподібний null: він представляє випадок, коли немає (дійсного) значення. Але це більш безпечна для типу альтернатива nullабо значення за замовчуванням, наприклад, DLT_UNKNOWNтому, що ви можете забути перевірити наявність nullабо DLT_UNKNOWNвипадків. Обидва вони є дійсними PcapLinkTypeзначеннями! На відміну від цього, ви не можете призначити Optional<PcapLinkType>значення змінній типу PcapLinkType. Optionalспочатку перевіряє наявність дійсного значення.

Звичайно, якщо ви хочете зберегти DLT_UNKNOWNдля зворотної сумісності або з будь-якої іншої причини, ви все одно можете використовувати Optionalнавіть у цьому випадку, використовуючи, orElse()щоб вказати його як значення за замовчуванням:

public enum PcapLinkType {
  DLT_NULL(0),
  DLT_EN3MB(2),
  DLT_AX25(3),
  /*snip, 200 more enums, not always consecutive.*/
  DLT_UNKNOWN(-1);

  private final int value;
  private PcapLinkType(int value) { this.value = value; }

  private static final Map<Integer, PcapLinkType> map;
  static {
    map = Arrays.stream(values())
        .collect(Collectors.toMap(e -> e.value, e -> e));
  }

  public static PcapLinkType fromInt(int value) {
    return Optional.ofNullable(map.get(value)).orElse(DLT_UNKNOWN);
  }
}

3

Ви можете додати статичний метод у свій перелік, який приймає an intяк параметр і повертає a PcapLinkType.

public static PcapLinkType of(int linkType) {

    switch (linkType) {
        case -1: return DLT_UNKNOWN
        case 0: return DLT_NULL;

        //ETC....

        default: return null;

    }
}

Краще не забудьте додати запис до цього switchтвердження, якщо ви додасте нове перерахування. Не ідеально, ІМХО.
dty

1
@dty Отже, ви вважаєте, що накладні витрати на HashMap переважають необхідність додавати новий випадок до оператора switch?
розчавити

1
Я вважаю, що я вважаю за краще писати код, який допомагає мені не робити помилок, і тому, швидше за все, він буде правильним, перш ніж зосередитись на мікропродуктивності пошуку хешу.
dty

3

Це те, що я використовую:

public enum Quality {ENOUGH,BETTER,BEST;
                     private static final int amount = EnumSet.allOf(Quality.class).size();
                     private static Quality[] val = new Quality[amount];
                     static{ for(Quality q:EnumSet.allOf(Quality.class)){ val[q.ordinal()]=q; } }
                     public static Quality fromInt(int i) { return val[i]; }
                     public Quality next() { return fromInt((ordinal()+1)%amount); }
                    }

Використання порядкових було визнано як погана практика, загалом, краще уникати.
Рафаель

1
static final PcapLinkType[] values  = { DLT_NULL, DLT_EN10MB, DLT_EN3MB, null ...}    

...

public static PcapLinkType  getPcapLinkTypeForInt(int num){    
    try{    
       return values[int];    
    }catch(ArrayIndexOutOfBoundsException e){    
       return DLT_UKNOWN;    
    }    
}    

1
Дорого, якщо телефонувати багато. Потрібно пам’ятати про оновлення масиву (чому він у вас є, коли перелічення визначають .values()метод?).
dty

@dty це спроба / зловити? Я думаю, було б справедливішим сказати, що це дорого, якщо багато значень потрапляють у категорію DLT_UNKNOWN.
nsfyn55

1
Я справді здивований, коли рішення масиву проголосували проти, а рішення карти - за. Мені тут не подобається --int, але це, очевидно, друкарська помилка.
18446744073709551615

Я бачу: вони хочуть nullзамість DLT_UKNOWN:)
18446744073709551615

1
Чому ні static final values[] = PcapLinkType.values()?
18446744073709551615

0

Немає можливості елегантно обробляти цілочисельні перелічені типи. Ви можете подумати використовувати замість вашого рішення перелічення на основі рядків. Постійно не кращий спосіб, але він все ще існує.

public enum Port {
  /**
   * The default port for the push server.
   */
  DEFAULT("443"),

  /**
   * The alternative port that can be used to bypass firewall checks
   * made to the default <i>HTTPS</i> port.
   */
  ALTERNATIVE("2197");

  private final String portString;

  Port(final String portString) {
    this.portString = portString;
  }

  /**
   * Returns the port for given {@link Port} enumeration value.
   * @return The port of the push server host.
   */
  public Integer toInteger() {
    return Integer.parseInt(portString);
  }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.