Can a java compiler optimize this code away? - java

Is a java compiler or runtime ( or any other language compiler ) smart enough to realize branch 3 can never happen , and optimize it away? I've seen this kind of "defensive programming" with many beginning developers, and wonder if this dead weight stays in the bytecode.
import java.util.Random;
class Example
{
public static void main(String[] args) {
int x = new Random().nextInt() % 10;
if ( x < 5 )
{
System.out.println("Case 1");
}
else
if ( x >= 5 )
{
System.out.println("Case 2");
}
else
{
System.out.println("Case 3");
}
}
}
or even this more blunt case
boolean bool = new Random().nextBoolean();
if ( bool )
{
System.out.println("Case 1");
}
else
if ( bool )
{
System.out.println("Case 2");
}

The Java 8 compiler I have doesn't seem to optimize it away. Using "javap -c" to examine the byte code after compiling:
public static void main(java.lang.String[]);
Code:
0: new #2 // class java/util/Random
3: dup
4: invokespecial #3 // Method java/util/Random."<init>":()V
7: invokevirtual #4 // Method java/util/Random.nextInt:()I
10: bipush 10
12: irem
13: istore_1
14: iload_1
15: iconst_5
16: if_icmpge 30
19: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
22: ldc #6 // String Case 1
24: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
27: goto 54
30: iload_1
31: iconst_5
32: if_icmplt 46
35: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
38: ldc #8 // String Case 2
40: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
43: goto 54
46: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
49: ldc #9 // String Case 3
51: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
54: return
}
The string "Case 3" still exists in the byte code.

Related

Does Java compiler optimize stream filtering?

Let's have a case:
x.stream().filter(X::isFlag).filter(this::isOtherFlag).reduce(...)
Does it differ from this one?
x.stream().filter(predicate(X::isFlag).and(this::isOtherFlag)).reduce(...)
Functionally, the two statements are equivalent. However, consider the two following blocks of code and their respective bytecodes:
public static void main(String[] args) {
List<String> list = List.of("Seven", "Eight", "Nine");
list.stream().filter(s -> s.length() >= 5)
.filter(s -> s.contains("n"))
.forEach(System.out::println);
}
public static void main(java.lang.String[]);
Code:
0: ldc #16 // String Seven
2: ldc #18 // String Eight
4: ldc #20 // String Nine
6: invokestatic #22 // InterfaceMethod java/util/List.of:(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/util/List;
9: astore_1
10: aload_1
11: invokeinterface #28, 1 // InterfaceMethod java/util/List.stream:()Ljava/util/stream/Stream;
16: invokedynamic #35, 0 // InvokeDynamic #0:test:()Ljava/util/function/Predicate;
21: invokeinterface #36, 2 // InterfaceMethod java/util/stream/Stream.filter:(Ljava/util/function/Predicate;)Ljava/util/stream/Stream;
26: invokedynamic #42, 0 // InvokeDynamic #1:test:()Ljava/util/function/Predicate;
31: invokeinterface #36, 2 // InterfaceMethod java/util/stream/Stream.filter:(Ljava/util/function/Predicate;)Ljava/util/stream/Stream;
36: getstatic #43 // Field java/lang/System.out:Ljava/io/PrintStream;
39: invokedynamic #52, 0 // InvokeDynamic #2:accept:(Ljava/io/PrintStream;)Ljava/util/function/Consumer;
44: invokeinterface #53, 2 // InterfaceMethod java/util/stream/Stream.forEach:(Ljava/util/function/Consumer;)V
49: return
-
public static void main(String[] args) {
List<String> list = List.of("Seven", "Eight", "Nine");
list.stream().filter(s -> s.length() >= 5 && s.contains("n"))
.forEach(System.out::println);
}
public static void main(java.lang.String[]);
Code:
0: ldc #16 // String Seven
2: ldc #18 // String Eight
4: ldc #20 // String Nine
6: invokestatic #22 // InterfaceMethod java/util/List.of:(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/util/List;
9: astore_1
10: aload_1
11: invokeinterface #28, 1 // InterfaceMethod java/util/List.stream:()Ljava/util/stream/Stream;
16: invokedynamic #35, 0 // InvokeDynamic #0:test:()Ljava/util/function/Predicate;
21: invokeinterface #36, 2 // InterfaceMethod java/util/stream/Stream.filter:(Ljava/util/function/Predicate;)Ljava/util/stream/Stream;
26: getstatic #42 // Field java/lang/System.out:Ljava/io/PrintStream;
29: invokedynamic #51, 0 // InvokeDynamic #1:accept:(Ljava/io/PrintStream;)Ljava/util/function/Consumer;
34: invokeinterface #52, 2 // InterfaceMethod java/util/stream/Stream.forEach:(Ljava/util/function/Consumer;)V
39: return
We can see that, in the second example, one call to invokedynamic and invokeinterface are missing (which makes sense as we omitted a call to filter). I'm sure someone could assist with me with the static analysis of this bytecode (I can post verbose files if needed), but the Java compiler clearly treats the single call to filter as a single Predicate<String> rather than splitting it at the operator &&, shortening the bytecode slightly.

Why can't I have a duplicate case in my switch statement?

I understand that this will fail to compile:
int caseNum = 2;
switch(caseNum)
{
case 2:
System.out.println("Happy");
break;
case 2:
System.out.println("Birthday");
break;
case 2:
System.out.println("To the ground!");
break;
default:
System.out.println("<3");
break;
}
I know that the case statements are conflicting and that the compiler "doesn't know which 'case 2' I am talking about". A few peers of mine and myself were wondering behind the scenes what the conflict is and had heard that switch statements are converted into hash-maps. Is that the case, does a switch statement become a hash-map during compile time and the conflict in the mapping create the error?
So far I have looked around Stack Overflow and Google for an answer, and the information must be out there already, but I am unsure how to express the question correctly it would appear. Thanks in advance!
A hash map is just one way that a switch statement could be compiled, but in any case, you can imagine having duplicate cases as trying to have multiple values for the same key in a regular HashMap. The compiler doesn't know which one of the values corresponds to the key, and so emits an error.
switch statements could also be compiled into a jump table, in which case this is still ambiguous for a very similar reason -- you have multiple different possibilities for the same jump location.
switch statements could also be compiled into a binary search, in which case you still have the same problem -- multiple different results for the same key being searched for.
Just in case you were curious, I did a small test case to see what javac would compile the switch to. From this (slightly modified) source:
public static void main(final String[] args) {
final int caseNum = 2;
switch (caseNum) {
case 1:
System.out.println("Happy");
break;
case 2:
System.out.println("Birthday");
break;
case 3:
System.out.println("To the ground!");
break;
default:
System.out.println("<3");
break;
}
}
You get this bytecode:
public static void main(java.lang.String[]);
Code:
0: iconst_2
1: tableswitch { // 1 to 3
1: 28
2: 39
3: 50
default: 61
}
28: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
31: ldc #3 // String Happy
33: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
36: goto 69
39: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
42: ldc #5 // String Birthday
44: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
47: goto 69
50: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
53: ldc #6 // String To the ground!
55: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
58: goto 69
61: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
64: ldc #7 // String <3
66: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
69: return
So at least for this small switch, a jump table seems to be used. I have heard that the way switches are compiled depends partly on their size, but I don't know the exact point at which the implementation changes. 20 cases still seem to be implemented as a jump table...
Turns out String switch statements are implemented a bit differently. From this source:
public static void main(final String[] args) {
final String caseNum = "2";
switch (caseNum) {
case "1":
System.out.println("Happy");
break;
case "2":
System.out.println("Birthday");
break;
case "3":
System.out.println("To the ground!");
break;
default:
System.out.println("<3");
break;
}
}
You get this bytecode:
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String 2
2: astore_2
3: iconst_m1
4: istore_3
5: aload_2
6: invokevirtual #3 // Method java/lang/String.hashCode:()I
9: tableswitch { // 49 to 51
49: 36
50: 50
51: 64
default: 75
}
36: aload_2
37: ldc #4 // String 1
39: invokevirtual #5 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
42: ifeq 75
45: iconst_0
46: istore_3
47: goto 75
50: aload_2
51: ldc #2 // String 2
53: invokevirtual #5 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
56: ifeq 75
59: iconst_1
60: istore_3
61: goto 75
64: aload_2
65: ldc #6 // String 3
67: invokevirtual #5 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
70: ifeq 75
73: iconst_2
74: istore_3
75: iload_3
76: tableswitch { // 0 to 2
0: 104
1: 115
2: 126
default: 137
}
104: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
107: ldc #8 // String Happy
109: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
112: goto 145
115: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
118: ldc #10 // String Birthday
120: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
123: goto 145
126: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
129: ldc #11 // String To the ground!
131: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
134: goto 145
137: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
140: ldc #12 // String <3
142: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
145: return
So you still have jump tables, but you have two. Bit of a roundabout process -- take the hash code, switch on that, based on the case load another constant, and from that do a "normal" switch (I think). But same basic process, I suppose?
Historically, switch statements were/could be implemented as jump tables (i.e. a mapping of value to destination address). Depending on the implementation of the table, it may be impossible to have two entries for the same value pointing to different addresses. Even if it were possible, how would you handle that duplicate value? Return from the first handler and then go to second handler? Never execute the second handler?
It makes no sense to allow this.

Which code snippet executes faster?

I can do same thing by two types of code snippet.
First Way:
String makeDate = Integer.toString(now.year) + Integer.toString(now.month) + Integer.toString(now.monthDay);
Or Second Way:
String makeDate = now.year + "" + now.month + "" + now.monthDay;
My question is:
Which method is preferable [First way or Second way]?
Which code snippet will execute faster?
The two snippits you show are nearly identical.
a String in Java is immutable; it can't be changed. When using the concatenation operator (+) the compiler actually generates code using a StringBuilder
For example your second snippit becomes:
String makeDate = new StringBuilder()
.append(now.year)
.append("")
.append(now.month)
.append("")
.append(now.monthDay)
.toString();
You can look at the generated bytecode to see this. Java comes with a program javap that allows you to look at your compiled .class.
I created a simple main() to provide minimal bytecode:
public static void main(String[] args)
{
String makeDate = Integer.toString(1) + Integer.toString(1) + Integer.toString(1);
System.out.println(makeDate);
}
Which produces:
public static void main(java.lang.String[]);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: new #2 // class java/lang/StringBuilder
3: dup
4: invokespecial #3 // Method java/lang/StringBuilder."<init>":()V
7: iconst_1
8: invokestatic #4 // Method java/lang/Integer.toString:(I)Ljava/lang/String;
11: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
14: iconst_1
15: invokestatic #4 // Method java/lang/Integer.toString:(I)Ljava/lang/String;
18: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
21: iconst_1
22: invokestatic #4 // Method java/lang/Integer.toString:(I)Ljava/lang/String;
25: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
28: invokevirtual #6 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
31: astore_1
32: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
35: aload_1
36: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
39: return
Versus:
public static void main(String[] args)
{
int i = 1;
String makeDate = i + "" + i + "" + i;
System.out.println(makeDate);
}
Produces:
public static void main(java.lang.String[]);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=1
0: iconst_1
1: istore_1
2: new #2 // class java/lang/StringBuilder
5: dup
6: invokespecial #3 // Method java/lang/StringBuilder."<init>":()V
9: iload_1
10: invokevirtual #4 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
13: ldc #5 // String
15: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
18: iload_1
19: invokevirtual #4 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
22: ldc #5 // String
24: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
27: iload_1
28: invokevirtual #4 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
31: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
34: astore_2
35: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
38: aload_2
39: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
42: return
Technically the latter is probably faster at some scale that is nearly immeasurable (< 1ns) but for all practical purposes it doesn't matter; use whichever you like.

A Switch Java problem : case expressions must be constant expressions

I having a problem in my switch/case statement. The error says : "Case expressions must be constant expressions". I understand the error and I can resolve it using If but can someone tells me why the case expression must be constant in a switch/case.
A code example of my error :
public boolean onOptionsItemSelected(MenuItem item) {
int idDirectory = ((MenuItem) findViewById(R.id.createDirectory)).getItemId();
int idSuppression = ((MenuItem) findViewById(R.id.recycleTrash)).getItemId();
int idSeeTrash = ((MenuItem) findViewById(R.id.seeTrash)).getItemId();
switch (item.getItemId()) {
case idDirectory:
createDirectory(currentDirectory);
break;
case idSuppression:
recycleTrash();
break;
case idSeeTrash:
seeTrash();
break;
}
return super.onOptionsItemSelected(item);
}
Thx for your explanation !!
So it can be evaluated during the compilation phase ( statically check )
See: http://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.11 for a formal definition of the switch.
Additionally it may help you to understand better how that switch is transformed into bytecode:
class Switch {
void x(int n ) {
switch( n ) {
case 1: System.out.println("one"); break;
case 9: System.out.println("nine"); break;
default: System.out.println("nothing"); break;
}
}
}
And after compiling:
C:\>javap -c Switch
Compiled from "Switch.java"
class Switch extends java.lang.Object{
Switch();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
void x(int);
Code:
0: iload_1
1: lookupswitch{ //2
1: 28;
9: 39;
default: 50 }
28: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
31: ldc #3; //String one
33: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
36: goto 58
39: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
42: ldc #5; //String nine
44: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
47: goto 58
50: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
53: ldc #6; //String nothing
55: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
58: return
}
See that line marked as 1:
1: lookupswitch{ //2
1: 28;
9: 39;
default: 50 }
It evaluates the value and goes to some other line. For instance if value is 9 it will jump to instruction 39:
39: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
42: ldc #5; //String nine
44: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
47: goto 58
Which in turn jumps to instruction 58 :
58: return
All this wouldn't be possible if it was evaluated dynamically. That's why.
in eclipse IDE is simple ,in switch sentence CTRL + 1 and convert switch sentence - if-else sentence http://tools.android.com/tips/non-constant-fields
The idDirectory and others need to be a constant and not a declared variable. Switch will not work in this case, you need switch to if-else construct.
EDIT I see what OP meant. That is just how switch works in java language.

In a java enhanced for loop, is it safe to assume the expression to be looped over will be evaluated only once?

In the following:
for (String deviceNetwork : deviceOrganizer.getNetworkTypes(deviceManufacturer)) {
// do something
}
Is it safe to assume that deviceOrganizer.getNetworkTypes(deviceManufacturer) will be called only once?
Yes, absolutely.
From section 14.14.2 of the spec:
If the type of Expression is a subtype of Iterable, then let I be the type of the
expression Expression.iterator(). The enhanced for statement is equivalent to a basic for
statement of the form:
for (I #i = Expression.iterator(); #i.hasNext(); ) {
VariableModifiersopt Type Identifier = #i.next();
Statement
}
(The alternative deals with arrays.)
Note how Expression is only mentioned in the first part of the for loop expression - so it's only evaluated once.
Yes, give it a try:
public class ForLoop {
public static void main( String [] args ) {
for( int i : testData() ){
System.out.println(i);
}
}
public static int[] testData() {
System.out.println("Test data invoked");
return new int[]{1,2,3,4};
}
}
Output:
$ java ForLoop
Test data invoked
1
2
3
4
To complement what's been said and verify that the spec is doing what it says, let's look at the generated bytecode for the following class, which implements the old and new style loops to loop over a list returned by a method call, getList():
public class Main {
static java.util.List getList() { return new java.util.ArrayList(); }
public static void main(String[] args) {
for (Object o : getList()) {
System.out.print(o);
}
for (java.util.Iterator itr = getList().iterator(); itr.hasNext(); ) {
Object o = itr.next(); System.out.print(o);
}
}
}
Relevant parts of the output:
0: invokestatic #4; //Method getList
3: invokeinterface #5, 1; //InterfaceMethod java/util/List.iterator
8: astore_1
9: aload_1
10: invokeinterface #6, 1; //InterfaceMethod java/util/Iterator.hasNext
15: ifeq 35
18: aload_1
19: invokeinterface #7, 1; //InterfaceMethod java/util/Iterator.next
24: astore_2
25: getstatic #8; //Field java/lang/System.out
28: aload_2
29: invokevirtual #9; //Method java/io/PrintStream.print
32: goto 9
35: invokestatic #4; //Method getList
38: invokeinterface #10, 1; //InterfaceMethod java/util/List.iterator
43: astore_1
44: aload_1
45: invokeinterface #6, 1; //InterfaceMethod java/util/Iterator.hasNext
50: ifeq 70
53: aload_1
54: invokeinterface #7, 1; //InterfaceMethod java/util/Iterator.next
59: astore_2
60: getstatic #8; //Field java/lang/System.out
63: aload_2
64: invokevirtual #9; //Method java/io/PrintStream.print
67: goto 44
70: return
This shows that the first loop (0 to 32) and the second (35-67) are identical.
The generated bytecode is exactly the same.

Categories