jk_
jk_

Reputation: 5578

How to get Enum Value from index in Java?

I have an enum in Java:

public enum Months
{
    JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC
}

I want to access enum values by index, e.g.

Months(1) = JAN;
Months(2) = FEB;
...

How shall I do that?

Upvotes: 116

Views: 216382

Answers (5)

Talijanac
Talijanac

Reputation: 1157

The natural ordering of Java enums is by the order of the values declared in the enum. Unfortunately, using values() method will allocate new array on each invocation. Thus creating performance issues.

static initializer can assign ordinal to each enum member based on natural order. Thus we don't have to maintain order -> id mapping. Additionally using cached values() response solves issue with constant allocation.

enum Months {
    JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC;

    int ordinal;     // ordinal will start from 1

    // cached instance used as optimization
    final static Months[] values = Months.values();

    static {
        // ordinal is initialized based on enum natural order
        int counter = 1;
        for (Months m: values)
            m.ordinal = counter++;  
    }

    public static Months getMonth(int ordinal) {
        return values[ordinal - 1];
    }
}

I had similar issue rising using JDBC ResultSet. I wanted to use ResultSet's index methods for field access, but I did not want to stop using column names as column count is high. ResultSet is also numbered starting 1 as first element. Similar enum was a natural solution to the problem.

Upvotes: 1

Mirko Brandt
Mirko Brandt

Reputation: 121

I recently had the same problem and used the solution provided by Harry Joy. That solution only works with with zero-based enumaration though. I also wouldn't consider it save as it doesn't deal with indexes that are out of range.

The solution I ended up using might not be as simple but it's completely save and won't hurt the performance of your code even with big enums:

public enum Example {

    UNKNOWN(0, "unknown"), ENUM1(1, "enum1"), ENUM2(2, "enum2"), ENUM3(3, "enum3");

    private static HashMap<Integer, Example> enumById = new HashMap<>();
    static {
        Arrays.stream(values()).forEach(e -> enumById.put(e.getId(), e));
    }

    public static Example getById(int id) {
        return enumById.getOrDefault(id, UNKNOWN);
    }

    private int id;
    private String description;

    private Example(int id, String description) {
        this.id = id;
        this.description= description;
    }

    public String getDescription() {
        return description;
    }

    public int getId() {
        return id;
    }
}

If you are sure that you will never be out of range with your index and you don't want to use UNKNOWN like I did above you can of course also do:

public static Example getById(int id) {
        return enumById.get(id);
}

Upvotes: 3

Blauspecht
Blauspecht

Reputation: 113

I just tried the same and came up with following solution:

public enum Countries {
    TEXAS,
    FLORIDA,
    OKLAHOMA,
    KENTUCKY;

    private static Countries[] list = Countries.values();

    public static Countries getCountry(int i) {
        return list[i];
    }

    public static int listGetLastIndex() {
        return list.length - 1;
    }
}

The class has it's own values saved inside an array, and I use the array to get the enum at indexposition. As mentioned above arrays begin to count from 0, if you want your index to start from '1' simply change these two methods to:

public static String getCountry(int i) {
    return list[(i - 1)];
}

public static int listGetLastIndex() {
    return list.length;
}

Inside my Main I get the needed countries-object with

public static void main(String[] args) {
   int i = Countries.listGetLastIndex();
   Countries currCountry = Countries.getCountry(i);
}

which sets currCountry to the last country, in this case Countries.KENTUCKY.

Just remember this code is very affected by ArrayOutOfBoundsExceptions if you're using hardcoded indicies to get your objects.

Upvotes: 8

Trever Shick
Trever Shick

Reputation: 1784

Here's three ways to do it.

public enum Months {
    JAN(1), FEB(2), MAR(3), APR(4), MAY(5), JUN(6), JUL(7), AUG(8), SEP(9), OCT(10), NOV(11), DEC(12);


    int monthOrdinal = 0;

    Months(int ord) {
        this.monthOrdinal = ord;
    }

    public static Months byOrdinal2ndWay(int ord) {
        return Months.values()[ord-1]; // less safe
    }

    public static Months byOrdinal(int ord) {
        for (Months m : Months.values()) {
            if (m.monthOrdinal == ord) {
                return m;
            }
        }
        return null;
    }
    public static Months[] MONTHS_INDEXED = new Months[] { null, JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC };

}




import static junit.framework.Assert.assertEquals;

import org.junit.Test;

public class MonthsTest {

@Test
public void test_indexed_access() {
    assertEquals(Months.MONTHS_INDEXED[1], Months.JAN);
    assertEquals(Months.MONTHS_INDEXED[2], Months.FEB);

    assertEquals(Months.byOrdinal(1), Months.JAN);
    assertEquals(Months.byOrdinal(2), Months.FEB);


    assertEquals(Months.byOrdinal2ndWay(1), Months.JAN);
    assertEquals(Months.byOrdinal2ndWay(2), Months.FEB);
}

}

Upvotes: 22

Harry Joy
Harry Joy

Reputation: 59660

Try this

Months.values()[index]

Upvotes: 280

Related Questions