365

I've the enum type ReportTypeEnum that get passed between methods in all my classes but I then need to pass this on the URL so I use the ordinal method to get the int value. After I get it in my other JSP page, I need to convert it to back to an ReportTypeEnum so that I can continue passing it.

How can I convert ordinal to the ReportTypeEnum?

Using Java 6 SE.

QED
  • 9,803
  • 7
  • 50
  • 87
Lennie
  • 10,605
  • 5
  • 22
  • 13

15 Answers15

729

To convert an ordinal into its enum representation you might want to do this:

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

Please notice the array bounds.

Note that every call to values() returns a newly cloned array which might impact performance in a negative way. You may want to cache the array if it's going to be called often.

Code example on how to cache values().


This answer was edited to include the feedback given inside the comments

Mike Lowery
  • 2,630
  • 4
  • 34
  • 44
Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
  • 1
    I implemented this solution and it is not working for me. It returns out the ordinal value is not guaranteed to match the order in which the enumerated types are added. I don't know that is what this answer is advocating, but I wanted to warn people nonetheless – IcedDante Jul 14 '15 at 00:06
  • @IcesDante: the ordinal is certainly guaranteed to correspond to the order of the Enumeration values in the source. If you observe a different behaviour, then something else must be wrong. My answer above is however suboptimal for all the reasons laid out in the other answers. – Joachim Sauer Jul 14 '15 at 08:35
  • @JoachimSauer Maybe IcedDante means the ordinal may not match if it was produced and saved by an earlier version of the source, in which the enum values were in a different order. – LarsH Jun 18 '20 at 22:24
145

This is almost certainly a bad idea. Certainly if the ordinal is de-facto persisted (e.g. because someone has bookmarked the URL) - it means that you must always preserve the enum ordering in future, which may not be obvious to code maintainers down the line.

Why not encode the enum using myEnumValue.name() (and decode via ReportTypeEnum.valueOf(s)) instead?

oxbow_lakes
  • 133,303
  • 56
  • 317
  • 449
  • I agree, it's the better solution. – Joachim Sauer Mar 04 '09 at 12:57
  • 34
    What if you change the name of the enum (but keep the ordering)? – Arne Evertsson Nov 11 '09 at 15:06
  • 8
    @Arne - I think this is much less likely than some inexperienced person coming along and adding a `value` at either the start or its correct alphabetical/logical position. (By logical I mean for example `TimeUnit` values have a logical position) – oxbow_lakes Nov 11 '09 at 15:35
  • 8
    I certainly prefer to force the enums order rather than the name of my enum...this is why I prefer to store the ordinal rather than the name of the enum in the database. Furthermore, it's better to use int manipulation rather than String... –  Mar 15 '11 at 16:06
  • 11
    I agree. In a public API, changing the name of an Enum would break backward compatibility but changing the order would not. For that reason, it makes more sense to use the name as your "key" – Noel Oct 11 '11 at 21:31
  • 2
    This is a good warning for cases where a value will live longer than the application. By writing an enum's ordinal to a file or database, you are (perhaps unknowingly) assuming that the ordering of your enum's values will never change. But there are plenty of cases where an enum's value will only be used during a program's execution. So choose wisely and consider Joachim Sauer's reply. It actually answers the question at hand. ;) – spaaarky21 Jul 10 '13 at 23:35
  • 4
    Storing the ordinal makes it easier to translate your ideas to other languages. What if you need to write some component in C? – QED Mar 25 '15 at 16:38
  • 3
    Let's not guess the OP's persistence requirements from a tersely worded question. Maybe the value doesn't stay in the wire format for longer than a single user session. Or maybe it does. – Seva Alekseyev Jul 24 '15 at 16:29
  • I strongly disagree. The enum type was introduced e.g. in [Pascal](http://www.gnu-pascal.de/gpc/Enumerated-Types.html) exactly for the purpose of order, so that each and every enum value could be compared to one another. – Jagger Jun 30 '16 at 07:27
  • on Android, if you're storing the enum name and using proguard, you'll want to make sure proguard doesn't obfuscate the enum name. – Someone Somewhere Jun 28 '18 at 11:24
  • I doesn't matter if you use the name or ordinal as the identifier. If you rely on one of them to remain the same, write a unit test that verifies it's correct! This way accidental changes can get caught. – pvorb May 07 '21 at 23:39
  • This is not at all a bad idea, in fact, it is the optimal solution. Using the enum constant's name rather than the ordinal has the drawback **that the name cannot change down the line**. In either case, your hypothetical situation requires that developers be made aware of how the system works. Using the ordinal in the URL, however, is much shorter (assuming enum constants are descriptive). – Kröw Jan 14 '23 at 01:40
115

If I'm going to be using values() a lot:

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

Meanwhile wherever.java:

Suit suit = Suit.values[ordinal];

If you want the array to be private, be my guest:

private static final Suit values[] = values();
public static Suit get(int ordinal) { return values[ordinal]; }

...

Suit suit = Suit.get(ordinal);

Mind your array bounds.

QED
  • 9,803
  • 7
  • 50
  • 87
  • 4
    +1 this is by far the best solution IMHO because one can pass ordinals around especially in android.os.Message. – likejudo Jun 22 '17 at 17:14
  • 16
    This is very worrying because **arrays are mutable**. Even though values[] is final, it doesn't prevent `Suit.values[0] = Suit.Diamonds;` somewhere in your code. Ideally that will never happen, but the overall principle of *don't expose mutable fields* still holds. For this approach, consider using `Collections.unmodifiableList` or the like instead. – Mshnik Oct 13 '17 at 17:55
  • 1
    How about - private static final Suit values[] = values(); public static Suit[] getValues() { return values; } – Pratyush Jul 17 '18 at 12:41
  • 6
    @Pratyush that would make the array variable immutable, but not its contents. I could still do getValues()[0]=somethingElse; – Calabacin Dec 19 '18 at 14:35
  • 1
    Agree, thus the point is not expose the `Suit values[]` directly or indirectly (how was mentioned through `getValues()`), I work around with a public method where the `ordinal` value must be sent how an argument and return the `Suit` representation from `Suit values[]`. The point here (question's tile from the beginning) was create an enum type from an enum ordinal – Manuel Jordan Jul 23 '19 at 17:57
19

I agree with most people that using ordinal is probably a bad idea. I usually solve this problem by giving the enum a private constructor that can take for example a DB value then create a static fromDbValue function similar to the one in Jan's answer.

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

}

Now you can change the order without changing the lookup and vice versa.

Kevin Welker
  • 7,719
  • 1
  • 40
  • 56
jmkelm08
  • 677
  • 6
  • 15
  • THIS is the right answer. I'm surprised it got so few points compared to other more direct but potentially invalid answers (due to code changes in the future). – Calabacin Dec 19 '18 at 14:27
  • d= (◕‿↼ ) Only solution for flags where the integer can be anything (and is not ordered) like `0xDEAD` for example. – Top-Master Nov 15 '20 at 07:53
8

You could use a static lookup table:

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);
  }
}
Jan
  • 8,011
  • 3
  • 38
  • 60
  • 3
    See also [Enums](http://download.oracle.com/javase/1.5.0/docs/guide/language/enums.html). – trashgod Jun 24 '11 at 16:13
  • 13
    Wow! Just wow! This is neat, of course, but... you know - the C programmer inside me screams in pain seeing that you allocate a full-blown HashMap and perform lookups inside it all of it JUST to essentially manage 4 constants: spades, hearts, diamonds and clubs! A C programmer would allocate 1 byte for each: 'const char CLUBS=0;' etc... Yes,a HashMap lookup is O(1), but the memory and CPU overhead of a HashMap, in this case make it many orders of magnitude slower and resource hungry than calling .values() directly! No wonder Java is such a memory hog if people write like this... – Leszek Feb 27 '15 at 14:15
  • 3
    Not every program requires the performance of a triple A game. In many cases trading memory and CPU for type safety, readability, maintainability, cross platform support, garbage collection, etc... is justifiable. Higher level languages exist for a reason. – Jan Sep 02 '15 at 14:53
  • 4
    But if your key range is always `0...(n-1)`, then an array is less code and more readable as well; the performance boost is just a bonus. `private static final Suit[] VALUES = values();` and `public Suit fromOrdinal(int ordinal) { return VALUES[ordinal]; }`. Extra advantage: crashes immediately on invalid ordinals, rather than silently returning null. (Not always an advantage. But often.) – Thomas Aug 08 '16 at 08:05
  • Sorry, but no. Using a HashMap here is ludicrous. Use an array. – Stefan Reich Mar 14 '22 at 17:01
  • @Jan Which triple-A games are you referring to? Most triple A games can only get good performance on a bulky GPU exactly because large software firms take little care with their code, just like this. I can only see them disproving your point entirely. Besides, using an array and exposing its values with a function is not only faster, shorter, cleaner, smaller in terms of memory, but something that a developer would expect to see, and thus easier to deal with in a codebase. – Kröw Jan 14 '23 at 01:45
5

This is what I use. I make no pretense that it's far less "efficient" than the simpler solutions above. What it does do is provide a much clearer exception message than "ArrayIndexOutOfBounds" when an invalid ordinal value is used in the solution above.

It utilizes the fact that EnumSet javadoc specifies the iterator returns elements in their natural order. There's an assert if that's not correct.

The JUnit4 Test demonstrates how it's used.

 /**
 * 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);
}
gerardw
  • 5,822
  • 46
  • 39
4

Safety first (with Kotlin):

// Default to null
EnumName.values().getOrNull(ordinal)

// Default to a value
EnumName.values().getOrElse(ordinal) { EnumName.MyValue }
Westy92
  • 19,087
  • 4
  • 72
  • 54
1

This is what I do on Android with 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];
    }
}

it's short and I don't need to worry about having an exception in Proguard

Someone Somewhere
  • 23,475
  • 11
  • 118
  • 166
1

You can define a simple method like:

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

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

And use it like:

System.out.println(Alphabet.get(2));
Amir Fo
  • 5,163
  • 1
  • 43
  • 51
0
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];
  }
}

the test class

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

I appreciate that you share with us if you have a more efficient code, My enum is huge and constantly called thousands of times.

Ar maj
  • 1,974
  • 1
  • 16
  • 16
0

So one way is to doExampleEnum valueOfOrdinal = ExampleEnum.values()[ordinal]; which works and its easy, however, as mentioned before, ExampleEnum.values() returns a new cloned array for every call. That can be unnecessarily expensive. We can solve that by caching the array like so ExampleEnum[] values = values(). It is also "dangerous" to allow our cached array to be modified. Someone could write ExampleEnum.values[0] = ExampleEnum.type2; So I would make it private with an accessor method that does not do extra copying.

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

You would use ExampleEnum.value(ordinal) to get the enum value associated with ordinal

joe pelletier
  • 390
  • 2
  • 12
0

There is an Easy and Bad way and there is a fairly easy and right way.

First, the easy and bad (those are usually very popular). Enum class method returns an array of all available instances via the values() method and you can access the enum object via array index.

RenderingMode mode = RenderingMode.values()[index];

//Enum Class somewhere else
public enum RenderingMode
{
    PLAYING,
    PREVIEW,
    VIEW_SOLUTION;    
    
}
    

//RenderingMode.values()[0] will return RenderingMode.PLAYING
//RenderingMode.values()[1] will return RenderingMode.PREVIEW
//Why this is bad? Because it is linked to order of declaration.

//If you later changed the order here, it will impact all your existing logic around this.
public enum RenderingMode
    {
        
        PREVIEW,
        VIEW_SOLUTION,
        PLAYING;    
        
    }
 //Now
//RenderingMode.values()[0] will return RenderingMode.PREVIEW
//RenderingMode.values()[1] will return RenderingMode.VIEW_SOLUTION

Here is the right way to do it. Create a static method fromInt in your enum class.

public enum RenderingMode
{
    PLAYING,
    PREVIEW,
    VIEW_SOLUTION;

    public static RenderingModefromInt(int index)
    {
       //this is independent of order of declaration
        switch (index)
        {
            case 0: return PLAYING;
            case 1: return PREVIEW;
            case 2: return VIEW_SOLUTION;
        }
        //Consider throwing Exception here

        return null;
    }
}
Sandeep Dixit
  • 799
  • 7
  • 12
0
public enum Status {
    STATUS_1, STATUS_2, STATUS_3, STATUS_4;

    public static Status getStatusByOrdinal(int ordinal) {
        for (Status status : values()) {
            if (status.ordinal() == ordinal) {
                return status;
            }
        }
        return STATUS_1;
    }
}
Jeka_FRI
  • 71
  • 1
  • 9
0

In this way you can not depend on compile-time generics resolution(so having an enum class instance you can create whenever enum you want, even those types created with Class.forMame)

public Object getInstance(Class enumClazz, int ordinal) throws Exception {
    Object[] allEnums = enumClazz.getDeclaredMethod("values", Object[].class).invoke(null, null);
    return allEnums[ordinal];
}
msangel
  • 9,895
  • 3
  • 50
  • 69
-1

Every enum has name(), which gives a string with the name of enum member.

Given enum Suit{Heart, Spade, Club, Diamond}, Suit.Heart.name() will give Heart.

Every enum has a valueOf() method, which takes an enum type and a string, to perform the reverse operation:

Enum.valueOf(Suit.class, "Heart") returns Suit.Heart.

Why anyone would use ordinals is beyond me. It may be nanoseconds faster, but it is not safe, if the enum members change, as another developer may not be aware some code is relying on ordinal values (especially in the JSP page cited in the question, network and database overhead completely dominates the time, not using an integer over a string).

Matthieu
  • 2,736
  • 4
  • 57
  • 87
Tony BenBrahim
  • 7,040
  • 2
  • 36
  • 49
  • 2
    Because comparing integers is much faster than comparing strings? – HighCommander4 Aug 24 '13 at 04:55
  • 2
    But ordinals will change if someone modifies the enum (Adds/reorder memvers). Sometimes it is about safety and not speed, especially on a JSP page where the network latency is at 1,000,000 times the difference between comparing an array of integers (a string) and a single integer. – Tony BenBrahim Aug 25 '13 at 11:18
  • toString can be overridden, so it may not return the enum name. The method name( ) is what gives the enum name (it's final) – gerardw Feb 10 '14 at 14:21
  • code comments and application versions are also a thing (maybe a file format is simpler and smaller this way) – joe pelletier Jan 21 '19 at 16:01
  • Not sure why this was downvoted when it's essentially recommending the same thing as in oxbow_lakes' answer. Definitely safer than using the ordinal. –  Jul 17 '19 at 02:06