자바 : Enum 대 Int
Java에서 플래그를 사용할 때 두 가지 주요 접근 방식을 보았습니다. 하나는 int 값과 if-else 문 한 줄을 사용합니다. 다른 하나는 enum과 case-switch 문을 사용하는 것입니다.
플래그에 대해 enum과 int를 사용하는 것 사이에 메모리 사용량과 속도면에서 차이가 있는지 궁금합니다.
모두 ints
와 enums
스위치 또는 경우 - 당시 다른 및 메모리 사용을 모두 사용할 수 있습니다 또한 모두를위한 최소한의, 그리고 속도는 비슷합니다 - 당신이 제기 된 점에 그들 사이에 유의 한 차이가 없습니다.
그러나 가장 중요한 차이점은 유형 검사입니다. 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
패턴 사용에는 세 가지 주요 문제가 있습니다 .
- 문제는 컴파일 시간이 아니라 런타임에 발생 하므로 수정하는 데 더 많은 비용이 들고 원인을 찾기가 더 어려워집니다.
- 당신은 나쁜 입력을 처리하는 코드를 작성에있다 - 일반적
if-then-else
으로 최종else throw new IllegalArgumentException("Unknown color " + color);
- 비싼 다시 - 위의 클래스 코드는 비록 컴파일 -이 상수의 충돌을 방지 아무것도
YELLOW
와GREEN
모두 동일한 값이3
를 사용 enums
하면 다음 모든 문제를 해결할 수 있습니다.
- 유효한 값을 전달하지 않으면 코드가 컴파일되지 않습니다.
- 특별한 "잘못된 입력"코드가 필요하지 않습니다. 컴파일러가 대신 처리합니다.
- 열거 형 값은 고유합니다.
메모리 사용량과 속도는 중요한 고려 사항이 아닙니다. 어느 쪽이든 차이를 측정 할 수 없습니다.
열거 형은 적용 할 때 선호되어야한다고 생각합니다. 왜냐하면 선택된 값이 함께 모여 닫힌 집합을 구성한다는 사실을 강조하기 때문입니다. 가독성도 크게 향상되었습니다. 열거 형을 사용하는 코드는 코드 전체에 흩어져있는 stray int 값보다 더 자체 문서화됩니다.
열거 형을 선호합니다.
Enum을 사용하여 다음과 같은 비트 조합 플래그를 대체 할 수도 있습니다. int flags = FLAG_1 | FLAG_2;
대신 typesafe 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
대신 플래그를 사용하는 일부 코드가 표시되는 이유 중 하나는 enum
Java 1.5까지 Java에 열거 형이 없었기 때문입니다.
따라서 원래 Java의 이전 버전 용으로 작성된 코드를보고 있다면 int
패턴이 유일한 옵션이었습니다.
int
현대 자바 코드에서 플래그를 사용하는 것이 여전히 선호되는 곳은 매우 적지 만, 대부분의 경우 enum
제공하는 형식 안전성과 표현력으로 인해를 사용하는 것이 좋습니다 .
효율성 측면에서 정확히 어떻게 사용되는지에 따라 다릅니다. JVM은 두 유형을 모두 매우 효율적으로 처리하지만 int 메소드는 일부 사용 사례에서는 약간 더 효율적일 수 있지만 (객체가 아닌 기본으로 처리되기 때문에) 다른 경우에는 열거 형이 더 효율적입니다 (그렇지 않기 때문에) 권투 / 언 박싱 던질 필요가 없습니다).
실제 애플리케이션에서 효율성 차이가 어떤 식 으로든 눈에 띄는 상황을 찾기가 어려울 수 있으므로 코드의 품질 (가독성 및 안전성)에 따라 결정을 내려야합니다. 99 %의 시간 동안 열거 형을 사용합니다 .
마음에 베어 enums
유형의 안전, 그리고 다른 하나 열거의 값을 혼합 할 수 없습니다. 이것이 플래그 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;
enums
over 의 메모리 사용량 ints
은 무시할 수 있으며 유형 안전성 enums
제공은 최소한의 오버 헤드를 허용합니다.
예, 차이가 있습니다. 최신 64 비트 Java Enum 값은 기본적으로 객체에 대한 포인터이며 64 비트 (비 압축 작업)를 사용하거나 추가 CPU (압축 작업)를 사용합니다.
내 테스트에서 열거 형 (1.8u25, AMD FX-4100)에 대해 약 10 % 성능 저하가 나타났습니다 : 13k ns 대 14k ns
아래 테스트 소스 :
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;
}
}
질문에 대한 답변 : 아니요, Enum 클래스를로드하는 데 무시할 수있는 시간이 지나도 성능은 동일합니다.
다른 사람들이 언급했듯이 두 유형 모두 switch 또는 if else 문에서 사용할 수 있습니다. 또한 다른 사람들이 언급했듯이 int 플래그보다 Enums를 선호해야합니다. 해당 패턴을 대체하도록 설계되었으며 추가 안전을 제공하기 때문입니다.
그러나 고려할 더 나은 패턴이 있습니다. switch 문 / if 문이 속성으로 생성해야하는 값을 제공합니다.
이 링크를보십시오 : http://docs.oracle.com/javase/1.5.0/docs/guide/language/enums.html 행성에 질량과 반지름을 부여하기 위해 제공된 패턴에 주목하십시오. 이러한 방식으로 속성을 제공하면 열거 형을 추가 할 때 케이스를 다루는 것을 잊지 않을 것입니다.
가능한 경우 Enum을 사용하는 것을 좋아하지만 열거 형에서 정의한 여러 파일 유형에 대해 수백만 개의 파일 오프셋을 계산해야하는 상황이 있었고 오프셋 기준을 계산하기 위해 수천만 번 switch 문을 실행해야했습니다. enum 유형에. 다음 테스트를 실행했습니다.
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에서 1M으로 줄 였을 때 문자열과 열거 형은 int보다 약 두 배 더 오래 걸렸습니다. 이는 int에 비해 처음으로 문자열과 열거 형을 사용하는 데 약간의 오버 헤드가 있음을 나타냅니다.
이 질문은 오래되었지만 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);
참고 URL : https://stackoverflow.com/questions/9254637/java-enum-vs-int
'developer tip' 카테고리의 다른 글
C에서 free와 malloc은 어떻게 작동합니까? (0) | 2020.12.10 |
---|---|
PLIST를 JSON으로 변환하는 명령 줄 도구? (0) | 2020.12.10 |
줄 바꿈으로 분할하도록 IFS를 설정할 때 백 스페이스를 포함해야하는 이유는 무엇입니까? (0) | 2020.12.10 |
java.lang.RuntimeException : 처리기 (android.os.Handler)가 죽은 스레드에서 처리기로 메시지를 전송 (0) | 2020.12.10 |
이전 버전의 Android Studio에서 설정을 어디에서 가져 오나요? (0) | 2020.12.10 |