重複の可能性:
Java:If vs. Switch
プログラミングで観察されるすべての条件ステートメントについて、これらのブロックのどれが最も好ましいか:
- 三項演算子
- else-ブロックの場合
- スイッチブロック
前もって感謝します!
重複の可能性:
Java:If vs. Switch
プログラミングで観察されるすべての条件ステートメントについて、これらのブロックのどれが最も好ましいか:
前もって感謝します!
もちろん、さまざまな方法で比較を実装できます。
私はそれをこのようにしました:
共通ブロック:
int a = 42;
int k = 17;
もしも:
if (a == 42)
k+=4;
else k+=5;
場合:
switch (a) {
case 42: k+=4; break;
default: k+=5; break;
}
三元:
k += (a == 42) ? 4 : 5;
それらは同じバイトコードにコンパイルされません:
l *Tern*.class
-rw-r--r-- 1 stefan stefan 704 2012-04-27 14:26 CaseIfTern.class
-rw-r--r-- 1 stefan stefan 691 2012-04-27 14:26 IfTernCase.class
-rw-r--r-- 1 stefan stefan 728 2012-04-27 14:26 TernIfCase.class
ただし、2つだけではなく、複数のケースがある場合は、スイッチの利点が役立ちます。
と三元が2つ以上のケースでカスケードする場合。
しかし、それらは慣用的に/意味的に異なります。三項演算子は何かを返しますが、ifまたはスイッチは返しません。
したがって、何を比較する必要があるかはそれほど明確ではありません。
しかし、私は次の結果でベンチマークを作成しました。
0 if tern case
1 3.103 0.244 0.118
2 0.306 0.276 0.309
3 0.382 0.329 0.328
4 0.464 0.435 0.458
5 5.045 1.519 1.248
6 4.57 3.088 2.915
7 4.036 2.977 3.015
8 3.197 3.834 3.893
9 4.631 4.523 5.488
10 6.445 3.891 3.09
これは、それらが実際には大きな違いをもたらさないこと、およびキャッシュ効果が、VMを加熱した後でも、5Mの場合に影響を与えることを示しています。
実際の状況では、ほとんど何も起こらない100万回の呼び出しはめったにありません。しかし、何かが起こった場合、if / case/ternaryの時間はすぐに無関係になります。
これが私がテストしたコードです:
public class CaseTernIf
{
public static int aORbIf (int a) {
if (a == 2)
return 4;
else return 5;
}
public static int aORbTern (int a) {
return (a == 2) ? 4 : 5;
}
public static int aORbCase (int a) {
switch (a) {
case 2: return 4;
default: return 5;
}
}
}
テストコード(Scala)は次のとおりです。
object IfCaseTernBench extends Benchcoat [List[Int], Seq[Int]] {
type I=List[Int]
type O=Seq[Int]
val name = "CaseTern"
/** We return a List of random Ints numbers here. */
def iGenerator (n: Int) : I = (for (x <- 1 to n) yield math.abs (random.nextInt (3))).toList
def takePart (li: I, n: Int) : I = li.take (n)
/* Each algorithm is called by a mapping to a static method. */
def ifTest (i: I) : O = i.map (CaseTernIf.aORbIf)
def caseTest (i: I) : O = i.map (CaseTernIf.aORbCase)
def ternTest (i: I) : O = i.map (CaseTernIf.aORbTern)
// Map of Test names -> methods to test
val list2bench: List [(String, I => O)] = List (
"if test" -> ifTest _
, "case test" -> caseTest _
, "tern test" -> ternTest _
)
def test = {
list2bench.foreach (algo => println (algo._2))
}
}
そしてここにBenchCoatのソースがあります
三項演算子は、else-ifブロックが必要とするアセンブリで「goto」を必要としないという理由だけで最も効率的です。スイッチケースは、else-ifブロックよりも効率的ではないと思います。else-ifブロックで1つの値を比較している場合(基本的に、すべてのスイッチが最終的に実行しているため)。
ただし、別の考慮事項、つまり明快さを考慮に入れる必要があります。最も効率的な使用法を選択するためのより重要なことは、明確で簡潔なコードを書くことです。このためには、三項演算子ではなく、else-ifまたはswitchブロックをお勧めします。そうしないと返される値を探して目を交差させ始めるからです。
最新のコンパイラを使用している場合、大きな違いはありません。コンパイラがコードを最適化した後、ネイティブコードはほぼ同じになるはずです。
三項演算子は、if(something){yay} else {boo}を書くための省略形であり、より簡単な割り当てが実行されます。三項演算子はifelse構文に展開され、バイトコードに違いはありません。
私は次のテストを行いました:
public class IfElseSwichTernary {
public static int aORbIf(int a) {
int x = 0;
if (a == 2) {
x = 4;
} else if (a == 3) {
x = 5;
} else if (a == 4) {
x = 6;
} else {
x = 7;
}
return x;
}
public static int aORbTern(int a) {
int x = 0;
x = (a == 2) ? 4 : ((a == 3) ? 5 : ((a == 4) ? 6 : 7));
return x;
}
public static int aORbCase(int a) {
int x = 0;
switch (a) {
case 2:
x = 4;
break;
case 3:
x = 5;
break;
case 4:
x = 6;
break;
default:
x = 7;
}
return x;
}
}
そして、Javaデコンパイラーを使用して逆コンパイルし、次のコードを取得しました。
パブリッククラスIfElseSwichTernary{
public static int aORbIf(int a) {
int x = 0;
if (a == 2) {
x = 4;
} else if (a == 3) {
x = 5;
} else if (a == 4) {
x = 6;
} else {
x = 7;
}
return x;
}
public static int aORbTern(int a) {
int x = 0;
x = (a == 2) ? 4 : ((a == 3) ? 5 : ((a == 4) ? 6 : 7));
return x;
}
public static int aORbCase(int a) {
int x = 0;
switch (a) {
case 2:
x = 4;
break;
case 3:
x = 5;
break;
case 4:
x = 6;
break;
default:
x = 7;
}
return x;
}
}
これは、コンパイラが何も変更しないことを意味します(JVMを変換して命令を実行するときにJVMが変更されるかどうかはわかりません)
それが同じ論理で言うなら、私たちが3つ以上の条件を話しているとき、スイッチはパフォーマンスにもっと影響します。
これは、JVMの実装とバージョンによって異なりますが、ステートメントが最速であるかどうかは一般的には言えません。
たとえば、生成されるバイトコードもすべてのステートメントで同じである可能性があります。