[ACCEPTED]-Java: Enum vs. Int-enums

Accepted answer
Score: 133

Both ints and enums can use both switch or if-then-else, and 10 memory usage is also minimal for both, and 9 speed is similar - there's no significant 8 difference between them on the points you 7 raised.

However, the most important difference 6 is the type checking. Enums are checked, ints are 5 not.

Consider this code:

public class SomeClass {
    public static int RED = 1;
    public static int BLUE = 2;
    public static int YELLOW = 3;
    public static int GREEN = 3; // sic

    private int color;

    public void setColor(int color) {
        this.color = color;
    }   
}

While many clients 4 will use this properly,

new SomeClass().setColor(SomeClass.RED);

There is nothing 3 stopping them from writing this:

new SomeClass().setColor(999);

There are 2 three main problems with using the public static final pattern:

  • The problem occurs at runtime, not compile time, so it's going to be more expensive to fix, and harder to find the cause
  • You have to write code to handle bad input - typically a if-then-else with a final else throw new IllegalArgumentException("Unknown color " + color); - again expensive
  • There is nothing preventing a collision of constants - the above class code will compile even though YELLOW and GREEN both have the same value 3

If 1 you use enums, you address all these problems:

  • Your code won't compile unless you pass valid values in
  • No need for any special "bad input" code - the compiler handles that for you
  • Enum values are unique
Score: 13

Memory usage and speed aren't the considerations 9 that matter. You would not be able to measure 8 a difference either way.

I think enums should 7 be preferred when they apply, because the 6 emphasize the fact that the chosen values 5 go together and comprise a closed set. Readability 4 is improved a great deal, too. Code using 3 enums is more self-documenting than stray 2 int values scattered throughout your code.

Prefer 1 enums.

Score: 13

You may even use Enums to replace those 6 bitwise combined flags like int flags = FLAG_1 | FLAG_2;

Instead you 5 can use a typesafe EnumSet:

Set<FlagEnum> flags = EnumSet.of(FlagEnum.FLAG_1, FlagEnum.FLAG_2);

// then simply test with contains()
if(flags.contains(FlagEnum.FLAG_1)) ...

The documentation states 4 that those classes are internally optimized 3 as bit vectors and that the implementation 2 should be perform well enough to replace 1 the int-based flags.

Score: 7

One of the reasons you will see some code 21 using int flags instead of an enum is that Java 20 did not have enums until Java 1.5

So if you 19 are looking at code that was originally 18 written for an older version of Java, then 17 the int pattern was the only option available.

There 16 are a very small number of places where 15 using int flags is still preferable in modern 14 Java code, but in most cases you should 13 prefer to use an enum, due to the type safety 12 and expressiveness that they offer.

In terms 11 of efficiency, it will depend on exactly 10 how they are used. The JVM handles both 9 types very efficiently, but the int method 8 would likely be slightly more efficient 7 for some use cases (because they are handled 6 as primitive rather than objects), but in 5 other cases, the enum would be more efficient 4 (because it doesn't need to go throw boxing/unboxing).

You would be hard pressed to find a situation in which the efficiency difference would be in any way noticeable in a real world application, so 3 you should make the decision based on the 2 quality of the code (readability and safety), which 1 should lead you to use an enum 99% of the time.

Score: 4

Yes, there is a difference. Under modern 6 64-bit java Enum values are essentially 5 pointers to objects and they either take 4 64 bits (non-compressed ops) or use additional 3 CPU (compressed ops).

My test showed about 2 10% performance degradation for enums (1.8u25, AMD 1 FX-4100): 13k ns vs 14k ns

Test source below:

public class Test {

    public static enum Enum {
        ONE, TWO, THREE
    }

    static class CEnum {
        public Enum e;
    }

    static class CInt {
        public int i;
    }

    public static void main(String[] args) {
        CEnum[] enums = new CEnum[8192];
        CInt[] ints = new CInt[8192];

        for (int i = 0 ; i < 8192 ; i++) {
            enums[i] = new CEnum();
            ints[i] = new CInt();
            ints[i].i = 1 + (i % 3);
            if (i % 3 == 0) {
                enums[i].e = Enum.ONE;
            } else if (i % 3 == 1) {
                enums[i].e = Enum.TWO;
            } else {
                enums[i].e = Enum.THREE;
            }
        }
        int k=0; //calculate something to prevent tests to be optimized out

        k+=test1(enums);
        k+=test1(enums);
        k+=test1(enums);
        k+=test1(enums);
        k+=test1(enums);
        k+=test1(enums);
        k+=test1(enums);
        k+=test1(enums);
        k+=test1(enums);
        k+=test1(enums);

        System.out.println();

        k+=test2(ints);
        k+=test2(ints);
        k+=test2(ints);
        k+=test2(ints);
        k+=test2(ints);
        k+=test2(ints);
        k+=test2(ints);
        k+=test2(ints);
        k+=test2(ints);
        k+=test2(ints);

        System.out.println(k);



    }

    private static int test2(CInt[] ints) {
        long t;
        int k = 0;
        for (int i = 0 ; i < 1000 ; i++) {
            k+=test(ints);
        }

        t = System.nanoTime();
        k+=test(ints);
        System.out.println((System.nanoTime() - t)/100 + "ns");
        return k;
    }

    private static int test1(CEnum[] enums) {
        int k = 0;
        for (int i = 0 ; i < 1000 ; i++) {
            k+=test(enums);
        }

        long t = System.nanoTime();
        k+=test(enums);
        System.out.println((System.nanoTime() - t)/100 + "ns");
        return k;
    }

    private static int test(CEnum[] enums) {
        int i1 = 0;
        int i2 = 0;
        int i3 = 0;

        for (int j = 100 ; j != 0 ; --j)
        for (int i = 0 ; i < 8192 ; i++) {
            CEnum c = enums[i];
            if (c.e == Enum.ONE) {
                i1++;
            } else if (c.e == Enum.TWO) {
                i2++;
            } else {
                i3++;
            }
        }

        return i1 + i2*2 + i3*3;
    }

    private static int test(CInt[] enums) {
        int i1 = 0;
        int i2 = 0;
        int i3 = 0;

        for (int j = 100 ; j != 0 ; --j)
        for (int i = 0 ; i < 8192 ; i++) {
            CInt c = enums[i];
            if (c.i == 1) {
                i1++;
            } else if (c.i == 2) {
                i2++;
            } else {
                i3++;
            }
        }

        return i1 + i2*2 + i3*3;
    }
}
Score: 3

Bear in mind that enums are type-safe, and you 7 can't mix values from one enum with another. That's 6 a good reason to prefer enums over ints for flags.

On 5 the other hand, if you use ints for your constants, you 4 can mix values from unrelated constants, like 3 this:

public static final int SUNDAY = 1;
public static final int JANUARY = 1;

...

// even though this works, it's a mistake:
int firstMonth = SUNDAY;

The memory usage of enums over ints is negligible, and 2 the type safety enums provide makes the minimal 1 overhead acceptable.

Score: 2

Answer to your question: No, the after 15 a negligible time to load the Enum Class, the 14 performance is the same.

As others have stated 13 both types can be used in switch or if else 12 statements. Also, as others have stated, you 11 should favor Enums over int flags, because 10 they were designed to replace that pattern 9 and they provide added safety.

HOWEVER, there 8 is a better pattern that you consider. Providing 7 whatever value your switch statement/if 6 statement was supposed to produce as property.

Look 5 at this link: http://docs.oracle.com/javase/1.5.0/docs/guide/language/enums.html Notice the pattern provided 4 for giving the planets masses and radii. Providing 3 the property in this manner insures that 2 you won't forget to cover a case if you 1 add an enum.

Score: 2

I like using Enums when possible but I had 16 a situation where I was having to compute 15 millions of file offsets for different file 14 types which I had defined in an enum and 13 I had to execute a switch statement tens 12 of millions of times to compute the offset 11 base on on the enum type. I ran the following 10 test:

import java.util.Random;

public class switchTest { public 9 enum MyEnum { Value1, Value2, Value3, Value4, Value5 };

public static void main(String[] args)
{
    final String s1 = "Value1";
    final String s2 = "Value2";
    final String s3 = "Value3";
    final String s4 = "Value4";
    final String s5 = "Value5";

    String[] strings = new String[]
    {
        s1, s2, s3, s4, s5
    };

    Random r = new Random();

    long l = 0;

    long t1 = System.currentTimeMillis();

    for(int i = 0; i < 10_000_000; i++)
    {
        String s = strings[r.nextInt(5)];

        switch(s)
        {
            case s1:
                // make sure the compiler can't optimize the switch out of existence by making the work of each case it does different
                l = r.nextInt(5);
                break;
            case s2:
                l = r.nextInt(10);
                break;
            case s3:
                l = r.nextInt(15);
                break;
            case s4:
                l = r.nextInt(20);
                break;
            case s5:
                l = r.nextInt(25);
                break;
        }
    }

    long t2 = System.currentTimeMillis();

    for(int i = 0; i < 10_000_000; i++)
    {
        MyEnum e = MyEnum.values()[r.nextInt(5)];

        switch(e)
        {
            case Value1:
                // make sure the compiler can't optimize the switch out of existence by making the work of each case it does different
                l = r.nextInt(5);
                break;
            case Value2:
                l = r.nextInt(10);
                break;
            case Value3:
                l = r.nextInt(15);
                break;
            case Value4:
                l = r.nextInt(20);
                break;
            case Value5:
                l = r.nextInt(25);
                break;
        }
    }

    long t3 = System.currentTimeMillis();

    for(int i = 0; i < 10_000_000; i++)
    {
        int xx = r.nextInt(5);

        switch(xx)
        {
            case 1:
                // make sure the compiler can't optimize the switch out of existence by making the work of each case it does different
                l = r.nextInt(5);
                break;
            case 2:
                l = r.nextInt(10);
                break;
            case 3:
                l = r.nextInt(15);
                break;
            case 4:
                l = r.nextInt(20);
                break;
            case 5:
                l = r.nextInt(25);
                break;
        }
    }

    long t4 = System.currentTimeMillis();

    System.out.println("strings:" + (t2 - t1));
    System.out.println("enums  :" + (t3 - t2));
    System.out.println("ints   :" + (t4 - t3));
}

}

and 8 got the following results:

strings:442

enums 7 :455

ints :362

So from this I decided that 6 for me enums were efficient enough. When 5 I decreased the loop counts to 1M from 10M 4 the string and enums took about twice as 3 long as the int which indicates that there 2 was some overhead to using strings and enums 1 for the first time as compared to ints.

Score: 0

Even though this question is old, I'd like 4 to point out what you can't do with ints

public interface AttributeProcessor {
    public void process(AttributeLexer attributeLexer, char c);
}

public enum ParseArrayEnd implements AttributeProcessor {
    State1{
        public void process(AttributeLexer attributeLexer, char c) {
            .....}},
    State2{
        public void process(AttributeLexer attributeLexer, char c) {
            .....}}
}

And 3 what you can do is make a map of what value 2 is expected as a Key, and the enum as a 1 value,

Map<String, AttributeProcessor> map 
map.getOrDefault(key, ParseArrayEnd.State1).process(this, c);

More Related questions