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 */

12 Answers 12

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;
}
Sign up to request clarification or add additional context in comments.

9 Comments

updated with recommendations from dty, which was a good idea.
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.
allOf is only available for sets
Also, EnumMap uses the enums as the keys. In this case, the OP wants the enums as the values.
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.
|
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.

9 Comments

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.
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.
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 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.
@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, programering.com/a/MzNxQjMwATM.html
|
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.

3 Comments

Might be expensive if called a lot. Building a static map is likely to give better amortised cost.
@dty o(n) with n = 200 - i dont think its an issue
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"
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 */

3 Comments

you missed the comment /*snip, 200 more enums, not always consecutive.*/
just in case your enum value is transitivity from Zero, this is bad practice
a good reminder of what's possible but not adviseable
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);
    }
}

1 Comment

That's what you get for double checking your answer before posting. ;)
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);
  }
}

Comments

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

Comments

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;

    }
}

3 Comments

Better not forget to add an entry to that switch statement if you add a new enum. Not ideal, IMHO.
@dty So, you think the overhead of a HashMap outweighs the need to add a new case to a switch statement?
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.
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); }
                    }

1 Comment

Using ordinal has been identified as bad practice, in general, is better to avoid.
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;    
    }    
}    

5 Comments

Expensive if called a lot. Need to remember to update the array (why do you even have it when enums define a .values() method?).
@dty is it the try/catch? I think it would be fairer to say its expensive if a lot of the values fall in the DLT_UNKNOWN category.
I am really surprised to see an array solution voted down and a map solution voted up. What I do dislike here is --int, but it's obviously a typo.
I see: they want null in place of DLT_UKNOWN :)
Why not static final values[] = PcapLinkType.values() ?
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;
            }
        }
    }

2 Comments

Please explain what your code does and how it does it.
.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)." docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/…
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);
  }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.