118

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?

5
  • 19
    In computer science, indices start at 0, not 1 ;-) Commented Jul 14, 2011 at 11:59
  • 1
    Are you sure you want to? Generally you shouldn't be touching the ordinal, other than implementing low-level data structures (and then, use alternative mechanisms, such as name, for persistence). Commented Jul 14, 2011 at 12:20
  • You could also have used the constants in the java.util.Calendar class too. They are numbered 0 - 11 for Jan - Dec. Be careful of 12 as that is UnDecember (something to do with the lunar calendar). But I'm just curious why re-inventing the wheel of month constants that already comes "stock" in the JRE? Commented Jul 14, 2011 at 12:47
  • 2FredOverflow: Aggree, I used wrong indexing. 2Tom Hawtin: Yes, I am sure. I persist data with some framework and I get back integer index, not the enum. 2Chris Aldrich: This is just dummy example which does not match real case. Commented Jul 14, 2011 at 14:02
  • By the way, Java 8 and later comes with a Month enum built-in. Commented Apr 10, 2019 at 20:22

5 Answers 5

283

Try this

Months.values()[index]
Sign up to request clarification or add additional context in comments.

5 Comments

Note that will clone a copy of the values array each time, so if you are calling this in the inner loop of performance sensitive code you might want to make a static copy and use that.
I m confused, then why would I not want use an array instead ?
@AnudeepSamaiya may be we want to use proper enum constants(Months.JAN) in code instead of months[1] everywhere.
@Christopher Barber here's a one-liner for "making a static copy": public static final ArrayList<Months> ALL = new ArrayList<Month>() {{ for (Months m : Months.values()) add(m); }};, then you can access the elements with Months i = ALL.get(index)
It would be easier to simply use Months.values().clone() or if you are paranoid about mutability to wrap it in an immutable list (see Collections)
22

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

}

1 Comment

public static mutable (both array and non-final). Euw. And an IllegalArgumentException would make much more sense than returning a null bomb.
8

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.

1 Comment

Are you sure this works as is with the static block? .values() normally supplies a clone of the internal (static) array, not the mere reference. That array is presumably preallocated internally, sure, but it would be populated with nulls at the point of the static{} block (before the object had a chance to run any of the constructors). I might use .values(), but only on the first call to get(index), where it stuffs the array away for later indexing, or fill each value incrementally in the constructor.
3

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

1 Comment

I'm missing the need for this complexity. Why not keep an internal array of everything in the enum? (In addition to it's own internal array). It's easy, straightforward, and your getById() is always an array lookup, instead of an array lookup on a cloned array from .values() (which is the way most do it), or the internal hashmap lookup you're keeping.
1

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.

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.