90

I've an enum like this:

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

Now I get an int from external input and want the matching input - throwing an exception if a value does not exist is ok, but preferably I'd have it be DLT_UNKNOWN in that case.

int val = in.readInt();
PcapLinkType type = ???; /*convert val to a PcapLinkType */
Lyke
  • 4,575
  • 6
  • 27
  • 26

12 Answers12

110

You would need to do this manually, by adding a a static map in the class that maps Integers to enums, such as

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;
}
MeBigFatGuy
  • 28,272
  • 7
  • 61
  • 66
  • 1
    updated with recommendations from dty, which was a good idea. – MeBigFatGuy Mar 13 '11 at 22:20
  • I hope you ran my code through a compiler first... I just made it up off the top of my head. I know the technique works - I used it yesterday. But the code is on another machine and this one doesn't have my dev tools. – dty Mar 13 '11 at 22:23
  • 1
    allOf is only available for sets – MeBigFatGuy Mar 13 '11 at 22:29
  • 1
    Also, `EnumMap` uses the enums as the keys. In this case, the OP wants the enums as the values. – dty Mar 13 '11 at 22:30
  • 8
    This seems like a lot of unneeded overhead. Those who actually need this type of operation are probably in need of high performance because they are writing/reading from streams/sockets, in which case, the caching of `values()` (if your enum values are sequential) or a simple `switch` statement would beat this method handily. If you only have a handful of entries in your `Enum` then it doesn't make much sense to add the overhead of a HashMap simply for the convenience of not having to update the `switch` statement. This method may seem more elegant, but it's also wasteful. – crush Aug 06 '13 at 20:19
  • Agree with crush and also i believe that .values() is a rather "expensive" method call. I'll use a switch statement. – Jes Chergui Apr 24 '14 at 18:25
  • Agreed with crush. If you need to do a 'for' loop to make that map, you can just as well retrieve the value there. Though, @JesChergui, a switch-case is annoying since it'd need to be adapted if the enum is changed. Also, switch-case in modern programming is a VERY far call from the original elegance found in an exe after compiling it from in c++. Looking at how enums work in Java, I wouldn't be so sure that it's any more efficient than .values(). – Nyerguds Jun 02 '14 at 13:09
  • OMG just do `PcapLinkType type = PcapLinkType.values()[val];` – rustyx Jun 26 '15 at 12:30
  • @rustyx you missed the comment /*snip, 200 more enums, not always consecutive.*/ – MeBigFatGuy Jul 26 '15 at 15:35
33

There's a static method values() which is documented, but not where you'd expect it: 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];}
}

In principle, you can use just values()[i], but there are rumors that values() will create a copy of the array each time it is invoked.

18446744073709551615
  • 16,368
  • 4
  • 94
  • 127
  • 11
    According to Joshua Bloch _(Effective Java Book)_: **Never derive a value associated with an enum from its ordinal;** Your implementation shouldn't rely on the enums order. – stevo.mit Sep 11 '13 at 15:08
  • 5
    Implementation **of what?** If we implement some algorithm, the implementation shouldn't rely on the enums order _unless_ that order is documented. When we implement the enum itself, it is ok to use such implementation details, in the same way as it is ok to use class-private methods. – 18446744073709551615 Sep 12 '13 at 05:14
  • 1
    Don't agree. I believe **never** is meant regardless documentation. You should not use ordinals even when you implement enum yourself. It's a bad smell and it's error prone. I not an expert but I wouldn't argue with Joshua Bloch :) – stevo.mit Sep 12 '13 at 07:14
  • 4
    @stevo.mit have a look at the new enum java.time.Month in Java 8. The Month.of(int) static method does exactly what Joshua Bloch said you should "never" do. It returns a Month based on its ordinal. – Klitos Kyriacou Feb 19 '15 at 12:39
  • @KlitosKyriacou there might be some rare exceptions to this rule. Especially when you are concerned about the performance and you are extremely sure that your enum values wont change in time (like months of the year). – stevo.mit Feb 20 '15 at 08:34
  • 2
    @stevo.mit There are _ordered enums_ and _unordered enums_. (And _bitmask enums_ too.) It is simply incorrect to talk of them as just "enums". The decision of what expressive means to use must be based on the _level of abstraction_ you work on. It is indeed incorrect to use implementation details (expressive means from the lower level) or usage assumptions (expressive means from the higher level). As to "_never_", in human languages never never means never, because there always is some context. (Usually, in application programming, never...) BTW, http://www.programering.com/a/MzNxQjMwATM.html – 18446744073709551615 Feb 20 '15 at 10:26
  • BTW, nobody yet said the word ***maintainability*** here: the software must not break badly when someone changes the source. – 18446744073709551615 Feb 20 '15 at 10:32
  • @18446744073709551615 we didn't mention the word maintainability because it was in the forefront of our minds throughout this conversation and was therefore implied (at least that was the case with me). I agree with stevo.mit that there might be rare exceptions, but we should avoid it in most cases. By the way, I doubt Oracle used ordinals in java.time.Month for performance reasons. I think doing it the way Josh Bloch recommends in Effective Java probably gives just as good performance. And good point from 18446744073709551615 about ordered and unordered enums. – Klitos Kyriacou Feb 22 '15 at 12:27
  • There's no guarantee the index in the list is the same as the value of the enum at that index... – Nyerguds May 16 '17 at 08:12
16

You will have to make a new static method where you iterate PcapLinkType.values() and compare:

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

That would be fine if it is called rarely. If it is called frequently, then look at the Map optimization suggested by others.

Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
  • 4
    Might be expensive if called a lot. Building a static map is likely to give better amortised cost. – dty Mar 13 '11 at 22:24
  • @dty o(n) with n = 200 - i dont think its an issue – Bozho Mar 13 '11 at 22:48
  • 7
    That's a totally ridiculous statement without a feel for how frequently it's called. If it's called once, fine. If it's called for every packet whizzing past on a 10Ge network then making an algorithm 200x faster is very important. Hence why I qualified my statement with "if called a lot" – dty Mar 14 '11 at 07:46
11

if you have enum like this

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

then you can use it like

PcapLinkType type = PcapLinkType.values()[1]; /*convert val to a PcapLinkType */
Jack Gajanan
  • 1,596
  • 14
  • 18
10

You can do something like this to automatically register them all into a collection with which to then easily convert the integers to the corresponding enum. (BTW, adding them to the map in the enum constructor is not allowed. It's nice to learn new things even after many years of using 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);
    }
}
Community
  • 1
  • 1
Esko Luontola
  • 73,184
  • 17
  • 117
  • 128
6

I know this question is a few years old, but as Java 8 has, in the meantime, brought us Optional, I thought I'd offer up a solution using it (and Stream and 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 is like null: it represents a case when there is no (valid) value. But it is a more type-safe alternative to null or a default value such as DLT_UNKNOWN because you could forget to check for the null or DLT_UNKNOWN cases. They are both valid PcapLinkType values! In contrast, you cannot assign an Optional<PcapLinkType> value to a variable of type PcapLinkType. Optional makes you check for a valid value first.

Of course, if you want to retain DLT_UNKNOWN for backward compatibility or whatever other reason, you can still use Optional even in that case, using orElse() to specify it as the default value:

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);
  }
}
Brad Collins
  • 846
  • 9
  • 18
4

As @MeBigFatGuy says, except you can make your static {...} block use a loop over the values() collection:

static {
    for (PcapLinkType type : PcapLinkType.values()) {
        intToTypeMap.put(type.getValue(), type);
    }
}
dty
  • 18,795
  • 6
  • 56
  • 82
3

This is what I use:

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); }
                    }
18446744073709551615
  • 16,368
  • 4
  • 94
  • 127
3

You could add a static method in your enum that accepts an int as a parameter and returns a PcapLinkType.

public static PcapLinkType of(int linkType) {

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

        //ETC....

        default: return null;

    }
}
Buhake Sindi
  • 87,898
  • 29
  • 167
  • 228
  • Better not forget to add an entry to that `switch` statement if you add a new enum. Not ideal, IMHO. – dty Mar 13 '11 at 22:26
  • 1
    @dty So, you think the overhead of a HashMap outweighs the need to add a new case to a switch statement? – crush Aug 06 '13 at 19:44
  • 1
    I think I'd rather write code that helps me not make mistakes and is therefore more likely to be correct before I focus on the micro-performance of a hash lookup. – dty Aug 06 '13 at 20:59
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;    
    }    
}    
soufrk
  • 825
  • 1
  • 10
  • 24
nsfyn55
  • 14,875
  • 8
  • 50
  • 77
1

This might not be a great solution, but its working for me:

public enum Type {
        WATER, FIRE, GRASS;

        public static Type getType(int value){
            if(value==WATER.ordinal()){
                return WATER;
            }else if(value==FIRE.ordinal()){
                return FIRE;
            }else if(value==GRASS.ordinal()){
                return GRASS;
            }else {
                return null;
            }
        }
    }
Sisi
  • 41
  • 1
  • 5
  • Please explain what your code does and how it does it. – M-Chen-3 Apr 19 '21 at 23:03
  • .ordinal() retrieves the final int value of the enum's placement in the enum list. According to ORACLE : "Returns the ordinal of this enumeration constant (its position in its enum declaration, where the initial constant is assigned an ordinal of zero)." https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Enum.html#ordinal() – Sisi Apr 20 '21 at 21:26
0

There is no way to elegantly handle integer-based enumerated types. You might think of using a string-based enumeration instead of your solution. Not a preferred way all the times, but it still exists.

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);
  }
}
Buğra Ekuklu
  • 3,049
  • 2
  • 17
  • 28