Java:Enum和Int
在Java中使用标志时,我看到了两种主要的方法。 一个使用int值和一行if-else语句。 另一种是使用枚举和case-switch语句。
我想知道在内存使用和速度之间使用枚举和整数标志之间的差异?
ints
和enums
都可以同时使用switch和if-then-else,而且内存的使用对于两者来说都是最小的,而且速度也是相似的 – 在你提出的观点上,它们之间没有显着的区别。
但是,最重要的区别是types检查。 Enums
被检查, ints
不是。
考虑这个代码:
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; } }
虽然很多客户会正确使用这个,
new SomeClass().setColor(SomeClass.RED);
没有什么能阻止他们写这个:
new SomeClass().setColor(999);
使用public static final
模式有三个主要问题:
- 问题出现在运行时 ,而不是编译时间,所以修复将会更加昂贵,而且难以find原因
- 你必须编写代码来处理不好的input – 通常是一个
if-then-else
,最后else throw new IllegalArgumentException("Unknown color " + color);
– 又贵 - 没有什么能够防止常量碰撞 – 即使
YELLOW
和GREEN
都具有相同的值3
,上面的类代码也会被编译
如果你使用enums
,你可以解决所有这些问题:
- 除非您传入有效值,否则您的代码将无法编译
- 不需要任何特殊的“糟糕的input”代码 – 编译器会为你处理
- 枚举值是唯一的
您甚至可以使用枚举来replace那些按位组合的标志,如int flags = FLAG_1 | FLAG_2;
int flags = FLAG_1 | FLAG_2;
相反,你可以使用types安全的EnumSet :
Set<FlagEnum> flags = EnumSet.of(FlagEnum.FLAG_1, FlagEnum.FLAG_2); // then simply test with contains() if(flags.contains(FlagEnum.FLAG_1)) ...
该文档指出,这些类在内部优化为位向量,并且执行应该足够好,以取代基于int的标志。
内存使用率和速度不是重要的考虑因素。 你将无法通过任何方式来衡量差异。
我认为枚举在应用时应该是优先的,因为强调了选定的值汇集在一起构成一个封闭的集合。 可读性也提高了很多。 使用枚举的代码比分散在整个代码中的int值更具自我loggingfunction。
喜欢枚举。
你将会看到一些使用int
标志而不是enum
代码的原因之一是Java在Java 1.5之前没有枚举
因此,如果您正在查看最初为较早版本的Java编写的代码,那么int
模式是唯一可用的选项。
在现代Java代码中,使用int
标志的地方仍然非常less,但是在大多数情况下,由于types的安全性和performance力,您应该更喜欢使用enum
。
在效率方面,这取决于它们的使用方式。 JVM可以非常有效地处理这两种types,但是对于某些用例,int方法可能会稍微有效一些(因为它们是作为原语而不是对象处理的),但是在其他情况下,枚举会更有效率(因为它没有“吨需要去扔拳击/拆箱)。
你很难find一个效率差异在现实世界的应用程序中会以任何方式显而易见的情况 ,所以你应该根据代码的质量(可读性和安全性)做出决定,这会导致你使用枚举99%的时间 。
请记住, enums
是types安全的,并且不能将一个枚举的值与另一个枚举混合使用。 这是一个很好的理由,喜欢enums
标志的ints
。
另一方面,如果你使用ints
作为常量,你可以混合来自不相关常量的值,如下所示:
public static final int SUNDAY = 1; public static final int JANUARY = 1; ... // even though this works, it's a mistake: int firstMonth = SUNDAY;
ints
enums
的内存使用可以忽略不计,types安全enums
提供了最小的开销。
回答你的问题:不,加载Enum Class后的时间微乎其微,性能是一样的。
正如其他人所说,这两种types都可以用在switch或else语句中。 另外,正如其他人所说,你应该赞成枚举int标志,因为它们被devise来取代这种模式,他们提供了更多的安全性。
不过,你有一个更好的模式,你考虑。 提供switch语句/ if语句应该作为属性产生的任何值。
看看这个链接: http : //docs.oracle.com/javase/1.5.0/docs/guide/language/enums.html注意提供给行星群众和半径的模式。 以这种方式提供财产,保证你不会忘记覆盖一个案例,如果你添加一个枚举。
我喜欢尽可能使用枚举,但是我有一种情况,我不得不为不同的文件types计算数百万个文件偏移量,这些文件types是我在枚举中定义的,我必须执行一个switch语句数千万次以计算偏移量基数在枚举types上。 我跑了下面的testing:
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)); }
}
并得到以下结果:
串:442
枚举:455
整数:362
所以从这我决定对我来说枚举是足够有效的。 当我把循环次数从10M减less到1M时,string和枚举花费的时间大约是int的两倍,这表明与int相比,第一次使用string和枚举会有一些开销。
是的,有一个区别。 在现代64位java下,枚举值本质上是指向对象的指针,它们要么占用64位(非压缩操作数),要么使用额外的CPU(压缩操作数)。
我的testing显示枚举(1.8u25,AMD FX-4100)性能下降了10%:13k ns vs 14k ns
testing来源如下:
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 (ce == Enum.ONE) { i1++; } else if (ce == 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 (ci == 1) { i1++; } else if (ci == 2) { i2++; } else { i3++; } } return i1 + i2*2 + i3*3; } }
尽pipe这个问题已经很老了,但我想指出一下你不能用int来做什么
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) { .....}} }
而且你可以做的是制作一张关于什么样的价值的地图,以及作为一个价值的枚举,
Map<String, AttributeProcessor> map map.getOrDefault(key, ParseArrayEnd.State1).process(this, c);