Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way to get an enum by numeric id

Tags:

java

enums

I need to create an enum with about 300 values and have the ability to get its value by id (int). I currently have this:

public enum Country {
    DE(1), US(2), UK(3);

    private int id;
    private static Map<Integer, Country> idToCountry = new HashMap<>();
    static {
        for (Country c : Country.values()) {
            idToCountry.put(c.id, c);
        }
    }

    Country(int id) {
        this.id = id;
    }

    public static Country getById(int id) {
        return idToCountry.get(id);
    }
}

That enum is going to be used a lot, so I'm wondering if this is the best possible solution performance-wise.

I've read http://docs.oracle.com/javase/1.5.0/docs/guide/language/enums.html over and over again, but couldn't find the part that describes at what time the

static {

}

block is called, and if it is guaranted that this will be called only once. So - is it?

like image 998
Michael Avatar asked Jan 05 '17 10:01

Michael


People also ask

Can I make an enum with numbers?

Numeric enums are number-based enums i.e. they store string values as numbers. Enums are always assigned numeric values when they are stored. The first value always takes the numeric value of 0, while the other values in the enum are incremented by 1.

Can we get an enum by value?

Get the value of an EnumTo get the value of enum we can simply typecast it to its type. In the first example, the default type is int so we have to typecast it to int. Also, we can get the string value of that enum by using the ToString() method as below.


3 Answers

In case if the first country id is 0 and ids are incremented by 1, you may use the next approach:

  1. Cache enum values in array. Enum.values() returns elements in the same order as they declared in enum. But it should be cached, as it creates new array every time it is invoked.
  2. Get values from cached array by id, which will be array index.

Please, see the code below:

enum Country {
    A, B, C, D, E;
    private static final Country[] values = Country.values();

    public static Country getById(int id) {
        return values[id];
    }
}

UPDATE: To get Country's id, ordinal() method should be used. And to make getting id code clearer, the next method can be added to the enum:

public int getId() {
    return ordinal();
}
like image 158
Anton Dovzhenko Avatar answered Oct 02 '22 14:10

Anton Dovzhenko


Static initializer blocks are called once when the class is initialized. It's not guaranteed to be called once, but it will be unless you're doing something exotic with class loaders.

So, your approach is probably fine from a performance perspective. The only changes I'd propose would be to make your fields final.


An alternative way to represent the mapping could be to store elements in an array (or a list):

Country[] countries = new Countries[maxId + 1];
for (Country country : Country.values()) {
  countries[country.id] = country;
}

You could then look them up by element index:

System.out.println(countries[1]);  // DE.

This avoids the performance penalty of having to box the id in order to call idToCountry.get(Integer).

This of course requires you to have non-negative IDs (and ideally the IDs would be reasonably contiguous, to avoid having to store large runs of null between countries).

like image 33
Andy Turner Avatar answered Oct 02 '22 15:10

Andy Turner


First you don't need to have a static block to create the map. You can just add your code to constructor where each component adds itself to your map. Enum is ALWAYS a sigleton so your constructor is guaranteed to be called only once (per a enum value) Also you don't need to even have ID as Enum has method public final int ordinal() that returns its zero-based sequential number in the enum. In your case ordinals would be 0 for DE, 1 forUS and 2 UK.

Here is an example:

public enum Country {
DE, US, UK;

private static Map<Integer, Country> idToCountry = new HashMap<>();

    Country() {
       idToCountry.put(this.ordinal(), this);
    }

    public static Country getById(int id) {
        return idToCountry.get(id);
    }
}
like image 23
Michael Gantman Avatar answered Oct 02 '22 16:10

Michael Gantman