Java: Enum vs. Int

JavaEnums

Java Problem Overview


When using flags in Java, I have seen two main approaches. One uses int values and a line of if-else statements. The other is to use enums and case-switch statements.

I was wondering if there was a difference in terms of memory usage and speed between using enums vs ints for flags?

Java Solutions


Solution 1 - Java

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

However, the most important difference is the type checking. Enums are checked, ints are 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 will use this properly,

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

There is nothing stopping them from writing this:

new SomeClass().setColor(999);

There are 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 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

Solution 2 - Java

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

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

Prefer enums.

Solution 3 - Java

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

Instead you can use a typesafe [EnumSet][1]:

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 that those classes are internally optimized as bit vectors and that the implementation should be perform well enough to replace the int-based flags.

[1]: http://docs.oracle.com/javase/7/docs/api/java/util/EnumSet.html "EnumSet"

Solution 4 - Java

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

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

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

In terms of efficiency, it will depend on exactly how they are used. The JVM handles both types very efficiently, but the int method would likely be slightly more efficient for some use cases (because they are handled as primitive rather than objects), but in other cases, the enum would be more efficient (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 you should make the decision based on the quality of the code (readability and safety), which should lead you to use an enum 99% of the time.

Solution 5 - Java

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

My test showed about 10% performance degradation for enums (1.8u25, AMD 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;
	}
}

Solution 6 - Java

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

On the other hand, if you use ints for your constants, you can mix values from unrelated constants, like 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 the type safety enums provide makes the minimal overhead acceptable.

Solution 7 - Java

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

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

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

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

Solution 8 - Java

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

import java.util.Random;

public class switchTest { public 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 got the following results:

strings:442

enums :455

ints :362

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

Solution 9 - Java

Even though this question is old, I'd like 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 what you can do is make a map of what value is expected as a Key, and the enum as a value,

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionFlynnView Question on Stackoverflow
Solution 1 - JavaBohemianView Answer on Stackoverflow
Solution 2 - JavaduffymoView Answer on Stackoverflow
Solution 3 - Javathe-banana-kingView Answer on Stackoverflow
Solution 4 - JavaTimView Answer on Stackoverflow
Solution 5 - JavaSergey AlaevView Answer on Stackoverflow
Solution 6 - JavaÓscar LópezView Answer on Stackoverflow
Solution 7 - JavaJoeView Answer on Stackoverflow
Solution 8 - Javauser3708686View Answer on Stackoverflow
Solution 9 - JavaJohnView Answer on Stackoverflow