21

Given the following java enum:

public enum AgeRange {

   A18TO23 {
        public String toString() {        
            return "18 - 23";
        }
    },
   A24TO29 {
        public String toString() {        
            return "24 - 29";
        }
    },
   A30TO35 {
        public String toString() {        
            return "30 - 35";
        }
    },

}

Is there any way to convert a string value of "18 - 23" to the corresponding enum value i.e. AgeRange.A18TO23 ?

Thanks!

Jason Gritman
  • 5,251
  • 4
  • 30
  • 38
Kevin
  • 956
  • 3
  • 11
  • 24

6 Answers6

30

The best and simplest way to do it is like this:

public enum AgeRange {
    A18TO23 ("18-23"),
    A24TO29 ("24-29"),
    A30TO35("30-35");

    private String value;

    AgeRange(String value){
        this.value = value;
    }

    public String toString(){
        return value;
    }

    public static AgeRange getByValue(String value){
        for (final AgeRange element : EnumSet.allOf(AgeRange.class)) {
            if (element.toString().equals(value)) {
                return element;
            }
        }
        return null;
    }
}

Then you just need to invoke the getByValue() method with the String input in it.

Pops
  • 30,199
  • 37
  • 136
  • 151
sakana
  • 4,251
  • 5
  • 25
  • 20
  • 1
    I agree that it's nice to put the value in the constructor. For very large enums (and they would really have to be pretty big) it would make sense to use a map. I'd personally return from inside the loop as well, but then I've never been a fan of "return from one place regardless of readability" :) – Jon Skeet Oct 27 '08 at 16:12
  • One other point - it would be better to use EnumSet.allOf instead of AgeRange.values() as otherwise you create a new array for every call. – Jon Skeet Oct 27 '08 at 16:13
  • Very interesting. A lot of what's happening in the code is new to me. Thanks for the tip! – Kevin Oct 28 '08 at 01:33
  • instead of your method, `getByValue(String)`, shouldn't you just delegate to the enum's inherent `valueOf(String)`? reference: http://stackoverflow.com/questions/11914792/where-does-the-enum-valueofstring-method-come-from – liltitus27 Nov 19 '13 at 18:48
7

You could always create a map from string to value - do so statically so you only need to map it once, assuming that the returned string remains the same over time. There's nothing built-in as far as I'm aware.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • This is a much better way to handle this. The map is then created when the enum is, and operates much faster than iterating an array. – Spencer Kormos Oct 27 '08 at 15:36
  • I am considering this method as well, but I have only around 15 values in this enum. Is it still more efficient to create a map? – Kevin Oct 29 '08 at 02:43
  • Probably not, to be honest. Hash tables are great for large amounts of data, but I suspect comparing 15 values (at worst) will be about as fast as getting the hashcode, finding the right bucket etc. – Jon Skeet Oct 29 '08 at 06:24
4

According to effective java (2nd ed) item 30, it can be (it is much faster than the loop)

public enum AgeRange {
       A18TO23("18-23"),
       A24TO29("24-29"),
       A30TO35("30-35");

       private final String value;

       AgeRange(String value){
          this.value = value;
       }

       @Override public String toString(){
           return value;
       }

       private static final Map<String, AgeRange> stringToEnum =
           new HashMap<String, AgeRange>();

       static {
           for (AgeRange r : values()) {
               stringToEnum.put(r.toString(), r);
           }
       }

       public static AgeRange getByValue(String value){
           return stringToEnum.get(value);
       }
}
wsu_cic
  • 41
  • 1
2
for (AgeRange ar: EnumSet.allOf(AgeRange)) {
    if (ar.toString().equals(inString)) {
         myAnswer = ar;
         break;
    }
}

Or something like that? Just typed in, haven't run through a compiler. Forgive (comment on) typos...

Or use logic like this to build a map once. Avoid iteration at runtime. Good idea, Jon.

John M
  • 13,053
  • 3
  • 27
  • 26
2

The class overrides "toString()" - so, to get the reverse operation, you need to override valueOf() to translate the output of toString() back to the Enum values.

public enum AgeRange {

   A18TO23 {
        public String toString() {        
                return "18 - 23";
        }
        public AgeRange valueOf (Class enumClass, String name) {
                return A18T023
        }
    },

    .
    .
    .
}

Buyer beware - uncompiled and untested...

The mechanism for toString() and valueOf() is a documented part of the API

Ken Gentle
  • 13,277
  • 2
  • 41
  • 49
  • Should that second be valueOf method? – iny Oct 27 '08 at 15:14
  • 2
    -1 If I'm not mistaken, the advice on valueOf here is ... wrong, to be gentle ;-). `valueOf()` is static, and of no use in the body of a particular enum value. And even once all this is fixed, the "correct" implementation would be wrong, in that the compiler doesn't allow a static override on `static public valueOf(String)`. The common fix is to simply not use `valueOf` - use a different name. – Ed Staub Jan 26 '12 at 14:50
0

You could try something like the following?

static AgeRange fromString(String range) {
    for (AgeRange ageRange : values()) {
        if (range.equals(ageRange.toString())) {
            return ageRange;
        }
    }
    return null;   
}

Or, as others suggested, using a caching approach:

private static Map<String, AgeRange> map;

private static synchronized void registerAgeRange(AgeRange ageRange) {
    if (map == null) {
        map = new HashMap<String, AgeRange>();
    }
    map.put(ageRange.toString(), ageRange);
}

AgeRange() {
    registerAgeRange(this);
}

static AgeRange fromString(String range) {
    return map.get(range);
}
toolkit
  • 49,809
  • 17
  • 109
  • 135