18

Hey all. So I have a set of enums and a db with integers corresponding to those enums. Something like this, for example:

public static enum Day {
    SUNDAY(1), MONDAY(2), TUESDAY(3), WEDNESDAY(4), THURSDAY(5), FRIDAY(6), SATURDAY(7);

    public final int fId;

    private Day(int id) {
        this.fId = id;
    }
}

I also have a database which only refers to these days by integers, which correspond to their int in the enum set above. What I am looking to do is query a database, which will return an integer, and then set an enumerator to an object based on that integer returned from the db. I could do something like this:

public static Day getDay(int i) {
    switch(i) {
    case 1:
        return Day.SUNDAY;
    case 2: 
        return Day.MONDAY;
    //And so on
    }
}

But for an enum set with over 100 enums inside this doesn't seem very practical.

So is there a way to do this? Again my goal is to simply insert an int value and get back an enumerator without having to create a new method for the many enums in this set. Maybe I should just make this its own class rather than an enumerator, but I was hoping to do it this way. Thanks!

JMRboosties
  • 15,500
  • 20
  • 76
  • 116
  • Check out the answers and comments here: http://stackoverflow.com/questions/3943054/how-can-i-convert-from-a-value-to-an-enum – Soronthar Jun 02 '11 at 22:05

6 Answers6

13

Here's a simple implementation that will work:

public static enum Day {
    SUNDAY(1), MONDAY(2), TUESDAY(3), WEDNESDAY(4), THURSDAY(5), FRIDAY(6), SATURDAY(7);

    public final int fId;

    private Day(int id) {
        this.fId = id;
    }

    public static Day forInt(int id) {
        for (Day day : values()) {
            if (day.fId == id) {
                return day;
            }
        }
        throw new IllegalArgumentException("Invalid Day id: " + id);
    }
}

If you know that your enums start counting from 1, you can simply use this instead:

public static Day forInt(int id) {
    return values()[id - 1];
}
Bohemian
  • 412,405
  • 93
  • 575
  • 722
9

Enums have a built-in ID number, the "ordinal," that you can use for this purpose, as long as you have an easy way to get from the number that represents a given value in the database to the ordinal of the enum. Ordinals are assigned to enum values in the order they are declared in the source file, starting from 0, so in your example, SUNDAY would have an ordinal of 0, MONDAY of 1, etc.

In order to use this, you just have to convert the integer stored in the database to its corresponding ordinal, and then use it to access the enum value. So for your example, an implementation might be

private static Day[] values = Day.values();
public static Day getDay(int i) {
    return values[i - 1];
}

If the order of the enum values according to their database IDs is the same order with which they are declared in the source code, then you can pretty much just use the above method as-is (except for changing the class name). Otherwise, you may have to do something a little more complicated to map database values to ordinals; perhaps setting up an array, if the mapping is highly nonlinear.

P.S. And of course if you do this, there would be no use for the fId field.

Community
  • 1
  • 1
David Z
  • 128,184
  • 27
  • 255
  • 279
  • values() is a METHOD not a FIELD. The code needs to be: return values()[i - 1]; (not return value[i - 1];). Notice the brackets after "values" – Bohemian Jun 03 '11 at 00:33
  • 1
    The problem with this approach is that it's very easy to accidentally break the ordering by adding a new enum value in the middle. If you were only using the values at runtime, then it wouldn't matter, but in this case the OP says they must be kept in sync with an external database. – Matt Solnit Jun 03 '11 at 03:19
  • 1
    @Bohemian: no, `EnumClass.values()` is a method. `values` is a field which I defined in the first line of my code sample. (Note that this code sample is _not_ part of the enum class itself) – David Z Jun 03 '11 at 03:59
  • @Matt: yep, that's why I added the last paragraph about mapping database integers to ordinals. Of course I would certainly agree that for a highly nonlinear mapping (or one that is subject to frequent change), another method might be easier. – David Z Jun 03 '11 at 04:00
6

You enum class should have a lookup map built inside as a static variable. Hence your enum class should be something like.

public static enum Day {
    SUNDAY(1), MONDAY(2), TUESDAY(3), WEDNESDAY(4), THURSDAY(5), FRIDAY(6), SATURDAY(7);

    public final int fId;

    private Day(int id) {
        this.fId = id;
    }


   private static final Map<Integer, Day > lookup
   = new HashMap<Integer, Day >();

   static {
   for (Day s : EnumSet.allOf(Day.class))
         lookup.put(s.getIntValue(), s);
   }

   public static Day getValue(int intValue) {
      return lookup.get(intValue);
   }

}

Then to retrieve the correct enum based on an int i, you can just invoke

Day.getValue(i);

This approach will work even if the numbering is non-linear or has gaps in it.

Basanth Roy
  • 6,272
  • 5
  • 25
  • 25
1

Edited - cache created lazy.

You can create Map<Integer,Day> runtime and use that in getDay implementation.

public static enum Day {
    SUNDAY(1), MONDAY(2), TUESDAY(3), WEDNESDAY(4), THURSDAY(5), FRIDAY(6), SATURDAY(7);

    private static Map<Integer,Day> CACHE = null;

    private static void lazyFillCache() {
        if (CACHE != null) return;

        final Map<Integer,Day> c = new HashMap<Integer,Day>();
        for (final Day d : Day.values()) {
            c.put(d.fId, d);
        }

        CACHE = c;
    }

    public static Day getDay(int i) {
        lazyFillCache();
        return CACHE.get(i);
    }

    public final int fId;

    private Day(int id) {
        this.fId = id;
    }
}

Edit - Inspired by Tom Hawtins comment here, you can also use this implementation, which takes advantage of Java class loading capabilities:

public static enum Day {
    SUNDAY(1), MONDAY(2), TUESDAY(3), WEDNESDAY(4), THURSDAY(5), FRIDAY(6), SATURDAY(7);

    private static class Cache {
        private static Map<Integer,Day> CACHE = new HashMap<Integer,Day>();

        static {
            for (final Day d : Day.values()) {
                CACHE.put(d.fId, d);
            }
        }
    }

    public static Day getDay(int i) {
        return Cache.CACHE.get(i);
    }

    public final int fId;

    private Day(int id) {
        this.fId = id;
    }
}
Community
  • 1
  • 1
user124
  • 1,632
  • 1
  • 11
  • 11
  • That won't work! Enums are not available when the class's static initializers are run - try executing it for yourself. Day.values() is empty when that code runs – Bohemian Jun 02 '11 at 22:14
  • Yea, i guess it could happen, strangely it worked on my machine. Mybe some weird compiler optimization. Then i suggest initializing CACHE lazy in getDay method. – user124 Jun 02 '11 at 22:18
  • Looks like my previous implementation was actually possible, static block where cache filling happens must locate in end of everything else: http://stackoverflow.com/questions/536449/cannot-refer-to-the-static-enum-field-within-an-initializer – user124 Jun 02 '11 at 22:34
  • I prefer to set the cache up by putting to it in the private ctor. `private Day(int id) { this.fId = id; CACHE.put(id,this); }` – Andrew Lazarus Jun 02 '11 at 22:43
0

Day myDay = Day.values()[x] - x is the int value

Ivo Stoyanov
  • 16,256
  • 8
  • 62
  • 65
0

Day.values() will return a (zero-based) array of the enum values.

MRAB
  • 20,356
  • 6
  • 40
  • 33