This isn't meant to be subjective, I am looking for reasons based on resource utilisation, compiler performance, GC performance etc. rather than elegance. Oh, and the position of brackets doesn't count, so no stylistic comments please.
Take the following loop;
Integer total = new Integer(0);
Integer i;
for (String str : string_list)
{
i = Integer.parse(str);
total += i;
}
versus...
Integer total = 0;
for (String str : string_list)
{
Integer i = Integer.parse(str);
total += i;
}
In the first one i is function scoped whereas in the second it is scoped in the loop. I have always thought (believed) that the first one would be more efficient because it just references an existing variable already allocated on the stack, whereas the second one would be pushing and popping i each iteration of the loop.
There are quite a lot of other cases where I tend to scope variables more broadly than perhaps necessary so I thought I would ask here to clear up a gap in my knowledge. Also notice that assignment of the variable on initialisation either involving the new operator or not. Do any of these sorts of semi-stylistic semi-optimisations make any difference at all?
The second one is what I would prefer. There is no functional difference other than the scoping.
Setting the same variable in each iteration makes no difference because Integer is an immutable class. Now, if you were modifying an object instead of creating a new one each time, then there would be a difference.
And as a side note, in this code you should be using int and Integer.parseInt() rather than Integer and Integer.parse(). You're introducing quite a bit of unnecessary boxing and unboxing.
Edit: It's been a while since I mucked around in bytecode, so I thought I'd get my hands dirty again.
Here's the test class I compiled:
class ScopeTest {
public void outside(String[] args) {
Integer total = 0;
Integer i;
for (String str : args)
{
i = Integer.valueOf(str);
total += i;
}
}
public void inside(String[] args) {
Integer total = 0;
for (String str : args)
{
Integer i = Integer.valueOf(str);
total += i;
}
}
}
Bytecode output (retrieved with javap -c ScopeTest after compiling):
Compiled from "ScopeTest.java"
class ScopeTest extends java.lang.Object{
ScopeTest();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public void outside(java.lang.String[]);
Code:
0: iconst_0
1: invokestatic #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
4: astore_2
5: aload_1
6: astore 4
8: aload 4
10: arraylength
11: istore 5
13: iconst_0
14: istore 6
16: iload 6
18: iload 5
20: if_icmpge 55
23: aload 4
25: iload 6
27: aaload
28: astore 7
30: aload 7
32: invokestatic #3; //Method java/lang/Integer.valueOf:(Ljava/lang/String;)Ljava/lang/Integer;
35: astore_3
36: aload_2
37: invokevirtual #4; //Method java/lang/Integer.intValue:()I
40: aload_3
41: invokevirtual #4; //Method java/lang/Integer.intValue:()I
44: iadd
45: invokestatic #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
48: astore_2
49: iinc 6, 1
52: goto 16
55: return
public void inside(java.lang.String[]);
Code:
0: iconst_0
1: invokestatic #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
4: astore_2
5: aload_1
6: astore_3
7: aload_3
8: arraylength
9: istore 4
11: iconst_0
12: istore 5
14: iload 5
16: iload 4
18: if_icmpge 54
21: aload_3
22: iload 5
24: aaload
25: astore 6
27: aload 6
29: invokestatic #3; //Method java/lang/Integer.valueOf:(Ljava/lang/String;)Ljava/lang/Integer;
32: astore 7
34: aload_2
35: invokevirtual #4; //Method java/lang/Integer.intValue:()I
38: aload 7
40: invokevirtual #4; //Method java/lang/Integer.intValue:()I
43: iadd
44: invokestatic #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
47: astore_2
48: iinc 5, 1
51: goto 14
54: return
}
Contrary to my expectations, there was one difference between the two: in outside(), the variable i still took up a register even though it was omitted from the actual code (note that all the iload and istore instructions point one register higher).
The JIT compiler should make short work of this difference, but still you can see that limiting scope is a good practice.
(And with regards to my earlier side note, you can see that to add two Integer objects, Java must unbox both with intValue, add them, and then create a new Integer with valueOf. Don't do this unless absolutely necessary, because it's senseless and slower.)
The second one is far better because the first style is should only be used in C code as its mandatory. Java allows for inline declarations to minimize the scope of variables and you should take advantage of that. But you code can be further improved:
int total = 0;
for (String str: stringList) {
try {
total += Integer.valueOf(str);
} catch(NumberFormationException nfe) {
// more code to deal with the error
}
}
That follows the Java code style convention. Read the full guide here:
http://java.sun.com/docs/codeconv/html/CodeConvTOC.doc.html
It makes no significant difference apart from on the last iteration, when the reference is cleared quicker in the second example (and that would be my preference - not so much for that reason, but clarity.)
Keep the scope to the minimum possible. The hotspot VM does escape analysis to determine when references are no longer accessible, and on the basis of this allocates some objects on the stack rather than on the heap. Keeping scope as small as possible aids this process.
I would ask why you're using Integer instead of a simple int...or perhaps it's just by way of example?
The second is far better. Scoping variables as narrowly as possible makes the code far easier to read and maintain, which are much more important overall than the performance differences between these examples, which are trivial and easily optimized away.
Neither. Integer.valueOf(0); will use a reference to a cached 0. :)
Well, in this case, you're instantiating an Integer primitive every single time you say i = Integer.parseInt(str) (where i is an Integer), so (unless Java knows how to optimize it), both cases are almost equally inefficient. Consider using int instead:
int total = 0;
for (String str : string_list)
{
int i = Integer.parseInt(str);
total += i;
}
Now we're back to the question of whether to put the int declaration on the inside or outside. Assuming the Java compiler has a lick of decent optimization, I'd say it doesn't matter. Efficiency aside, it is considered good practice to declare variables as close as possible to their use.
The second one is the preferable of the two for readability, maintainability, and efficiency.
All three of these goals are achieved because you are succinctly explaining what you are doing and how your variables are being used. You are explaining this clearly to both developers and the compiler. When the variable i is defined in the for block everyone knows that it is safe to ignore it outside of the block and that the value is only valid for this iteration of the block. This will lead the to the Garbage Collector being able to be able to easily mark this memory to be freed.
I would suggest not using Integer for intermediate values. Accumulate the total as an int and after the loop create the Object or depend on auto-boxing.
Assuming you have positive numbers in your list and you're serious with
I am looking for reasons based on
resource utilisation, compiler
performance, GC performance etc.
rather than elegance.
You should implement it by yourself like:
import java.util.ArrayList;
import java.util.List;
public class Int {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("10");
list.add("20");
int total = 0;
for (String str : list) {
int val = 0;
for (char c : str.toCharArray()) {
val = val * 10 + (int) c - 48;
}
total += val;
}
System.out.print(total);
}
}
The only GC relevant thing would be toCharArray() which could be replaced by another loop using charAt()
The question of what variable scope to use is a readability issue more than anything else. The code is better understood when every variable is restricted to the scope where it is actually used.
Now, if we inspect the technical consequences of using wide/narrow scopes, I believe that there IS a performance/footpring advantage with narrow scopes. Consider the following method, where we have 3 local variables, belonging to one global scope:
private static Random rnd = new Random();
public static void test() {
int x = rnd.nextInt();
System.out.println(x);
int y = rnd.nextInt();
System.out.println(y);
int z = rnd.nextInt();
System.out.println(z);
}
If you diassemble this code (using javap -c -verbose {class name} for example), you will see that the compiler reserves 3 slots for local variables in the stack frame structure of the test() method.
Now, suppose that we add some artificial scopes:
public static void test() {
{
int x = rnd.nextInt();
System.out.println(x);
}
{
int y = rnd.nextInt();
System.out.println(y);
}
{
int z = rnd.nextInt();
System.out.println(z);
}
}
If you diassemble the code now, you will notice that the compiler reserves only 1 slot for local variables. Since the scopes are completely independent, each time x,y or z are used, the same slot #0 is used.
What does it mean?
1) Narrow scopes save stack space
2) If we are dealing with object variables, it means that the objects may become unreachable faster, therefore are eligible for GC sooner than otherwise.
Again,note that these 2 "advantages" are really minor, and the readability concern should be by far the most important concern.
Second one since you want to keep the scope of your variables as "inner" as possible. The advantage of smaller scope is less chance for collision. In your example, there's only a few lines so the advantage might not be so obvious. But if it's larger, having the smaller-scope variables definitely is more beneficial. If someone else later has to look at the code, they would have to scan all the way back to right outside the method definition to know what i is. The argument is not much different than that of why we want to avoid global variable.
Related
I am following an online course about data structures and algorithms. In that course, the instructor tells that the time complexities of following ways are different.
Method 1:
Declare:
int arr[]------------>O(1)
Instantiation:
arr = new int[size]------>O(1)
Initialization:
arr[0]=0;------------>O(1)
-------------->O(n)
arr[1]=1;------------>O(1)
Method 2:
Declaration,instantiation and initialization:
int arr[]={10,20,30}---------------->O(1)
I need to know that by following the second method can we optimise our program and how can it possible to tell that it's having O(1) ,what's the difference between both these methods.
I mean that I think although the second method is having fewer steps it internally follows all the steps which are in the first method, so it can't be O(1) it's also O(n) , Iam I correct?
Java’s array initializer syntax is just syntactic sugar for the instructions of allocating an array and assigning a value to each element.
This can be easily verified by the following code:
import java.io.IOException;
import java.nio.file.Paths;
public class ArrayInitializer {
public void form1() {
int arr[] = new int[3];
arr[0] = 10;
arr[1] = 20;
arr[2] = 30;
}
public void form2() {
int arr[] = { 10, 20, 30 };
}
public static void main(String[] args) throws Exception {
decompile();
}
private static void decompile() throws InterruptedException, IOException
{
new ProcessBuilder(Paths.get(System.getProperty("java.home"))
.getParent().resolve(Paths.get("bin", "javap")).normalize().toString(),
"-c", "-cp", System.getProperty("java.class.path"),
ArrayInitializer.class.getName())
.inheritIO()
.start().waitFor();
}
private ArrayInitializer() {}
}
which prints
Compiled from "ArrayInitializer.java"
public class ArrayInitializer {
public void form1();
Code:
0: iconst_3
1: newarray int
3: astore_1
4: aload_1
5: iconst_0
6: bipush 10
8: iastore
9: aload_1
10: iconst_1
11: bipush 20
13: iastore
14: aload_1
15: iconst_2
16: bipush 30
18: iastore
19: return
public void form2();
Code:
0: iconst_3
1: newarray int
3: dup
4: iconst_0
5: bipush 10
7: iastore
8: dup
9: iconst_1
10: bipush 20
12: iastore
13: dup
14: iconst_2
15: bipush 30
17: iastore
18: astore_1
19: return
public static void main(java.lang.String[]) throws java.lang.Exception;
Code:
0: invokestatic #22 // Method decompile:()V
3: return
}
So, if the compiled code does not differ, there can’t be a performance difference caused by the form of the source code.
This doesn’t preclude runtime optimizations made by the JVM, e.g. when it detects that this is an allocation followed by filling with values whose evaluation can not fail in a way that would leak an uninitialized array. But these optimizations would apply in either case, as the source code form doesn’t matter.
A practical example is discussed in this article regarding the toArray method of collections, whose performance turns out to be dependent on the JVM’s ability to recognize an array allocation which is immediately followed by a copy operation overwriting the entire created array.
It is possible to initialize an array in constant time. I do not know which, if any, JVM implementation do it, but it is possible. Since this technique requires 3 times as much memory, it may very well not be used by any JVM implementation.
Assuming the JVM do not use this technique, then
arr = new int[size]
and
int arr[]={10,20,30}
will both run in O (n) (in the first case, all element in the array need to be zeroed).
But in case this (or a similar technique) is used, your instructor is correct.
Edit
Without fancy initialization technique, the complexity is O (n) in both case. But the constant factor is probably different. So I did a benchmark using jmh with 100 sized arrays (sorry, I was to lazy to write longer arrays). The result is below:
# Run complete. Total time: 00:20:13
Benchmark Mode Cnt Score Error Units
ArrayInitBenchmark.initialize_and_set thrpt 200 32341391,483 ± 46429,821 ops/s
ArrayInitBenchmark.initialize_only thrpt 200 32523162,079 ± 34682,391 ops/s
ArrayInitBenchmark.initialize_with_zeros thrpt 200 36267571,539 ± 34839,701 ops/s
initialize_and_set is the OP’s first method.
initialize_only is the OP’s second method.
initialize_with_zeros is a mere new int[size].
As you can see, the second method is a bit faster, but less than 1% faster than the first one (32.3 M ops/s vs 32.5 M ops/s). That is, on my machine, with oracle JDK 1.8.0_201. (testing with Java 13 would have required me to setup a new jmh project, while I had everything already setup for Java 8).
So, pop()method in java framework java.util.Stack class looks like this:
#SuppressWarnings("unchecked")
public synchronized E pop() {
if (elementCount == 0) {
throw new EmptyStackException();
}
final int index = --elementCount;
final E obj = (E) elementData[index];
elementData[index] = null;
modCount++;
return obj;
}
The part that I have trouble in understanding is local variable index. It seems we don't need it. elementCount is a instance variable in Vector class which Stack class extended.
So my point is,
final int index = --elementCount;
final E obj = (E) elementData[index];
elementData[index] = null;
These 3 lines of code can be written like
final E obj = (E) elementData[--elementCount];
elementData[elementCount] = null;
which consumes less memory, because memory space for index local variable isn't used.
Also, I found this pattern along the java framework source code. For example add(E Object) method in java.util.ArrayList class looks :
#Override public boolean add(E object) {
Object[] a = array;
int s = size;
if (s == a.length) {
Object[] newArray = new Object[s +
(s < (MIN_CAPACITY_INCREMENT / 2) ?
MIN_CAPACITY_INCREMENT : s >> 1)];
System.arraycopy(a, 0, newArray, 0, s);
array = a = newArray;
}
a[s] = object;
size = s + 1;
modCount++;
return true;
}
in this example, array is a instance variable, and as you can see, a new local variable a is assigned to hold it.
Does anybody know about this? Big Thanks in advance. :)
Though this is a really old question, but I want to share some information I earned during my journey.
I could find some explanation about my question on Performance Tips on Android page. First see sample code from the page,
static class Foo {
int mSplat;
}
Foo[] mArray = ...
public void zero() {
int sum = 0;
for (int i = 0; i < mArray.length; ++i) {
sum += mArray[i].mSplat;
}
}
public void one() {
int sum = 0;
Foo[] localArray = mArray;
int len = localArray.length;
for (int i = 0; i < len; ++i) {
sum += localArray[i].mSplat;
}
}
public void two() {
int sum = 0;
for (Foo a : mArray) {
sum += a.mSplat;
}
}
According to the above page, zero() is slowest, one() is faster. Because it pulls everything out into local variables, avoiding the lookups.
I think this explanation might solve my second question, which was asking "a new local variable a is assigned to hold it. but why?"
I hope this might help someone who have the same curiosity.
[EDIT] Let me add some details about "lookups".
So if you compile above code and disassembles the class file with javap command with -c option, it will print out disassembled code, i.e., the instructions that comprise the Java bytecodes.
public void zero();
Code:
0: iconst_0 // Push int constant 0
1: istore_1 // Store into local variable 1 (sum=0)
2: iconst_0 // Push int constant 0
3: istore_2 // Store into local variable 2 (i=0)
4: goto 22 // First time through don't increment
7: iload_1
8: aload_0
9: getfield #14 // Field mArray:[LTest$Foo;
12: iload_2
13: aaload
14: getfield #39 // Field Test$Foo.mSplat:I
17: iadd
18: istore_1
19: iinc 2, 1
22: iload_2 // Push value of local variable 2 (i)
23: aload_0 // Push local variable 0 (this)
24: getfield #14 // Field mArray:[LTest$Foo;
27: arraylength // Get length of array
28: if_icmplt 7 // Compare and loop if less than (i < mArray.length)
31: return
public void one();
Code:
0: iconst_0 // Push int constant 0
1: istore_1 // Store into local variable 1 (sum=0)
2: aload_0 // Push this
3: getfield #14 // Field mArray:[LTest$Foo;
6: astore_2 // Store reference into local variable (localArray)
7: aload_2 // Load reference from local variable
8: arraylength // Get length of array
9: istore_3 // Store into local variable 3 (len = mArray.length)
10: iconst_0 // Push int constant 0
11: istore 4 // Store into local variable 4 (i=0)
13: goto 29 // First time through don't increment
16: iload_1
17: aload_2
18: iload 4
20: aaload
21: getfield #39 // Field Test$Foo.mSplat:I
24: iadd
25: istore_1
26: iinc 4, 1
29: iload 4 // Load i from local variable
31: iload_3 // Load len from local variable
32: if_icmplt 16 // // Compare and loop if less than (i < len)
35: return
These instructions are a bit unfamiliar, so I looked up in JVM spec documents. (If you are curious, especially chapter 3, Compiling for the Java Virtual Machine, and chapter 6, The Java Virtual Machine Instruction Set would be helpful).
I added comment to help you understand, but in a nut shell, method zero() should operate getfield instruction on every iteration. According to JVM spec documentation 3.8. Working with Class Instances section, getfield operation performs several jobs like below.
The compiler generates symbolic references to the fields of an
instance, which are stored in the run-time constant pool. Those
run-time constant pool items are resolved at run-time to determine the
location of the field within the referenced object.
These 3 lines of code can be written like
We're in the business of making a useful and extendable programs, and in order to acheive that we should make our life as Developers easy as we can.
If it takes me a 5 more seconds to read the code and i can simplify it, i would. Specially if it comes in the expense of a int memory.. hardly calls as Optimization.
in this example, array is a instance variable, and as you can see, a new local variable a is assigned to hold it. Does anybody know about this?
This is hardly calls as question, i believe you meant to phrase it like that:
Why does they used another reference to array called a if they could use array ?
Well, I truly can't see why, because they could have use the E type since it given to them. It may be a reason of Covariance and Contravariance but i'm not sure.
Tip: Also next time you add pieces of a language source code, it will be nice to know which JDK you are viewing and a link me very help.
Keep in mind that --elementCount does the assignment before decrement. That means the fragment:
final int index = --elementCount;
final E obj = (E) elementData[index];
elementData[index] = null;
Can be translated into
final int index = elementCount;
elementCount--;
final E obj = (E) elementData[index];
elementData[index] = null;
Which means in your proposed replacement "elementData[--elementCount]" and "elementData[elementCount]" do not reference the same item. Your proposed replacement is not equivalent.
Hope this helps.
So, lets say I have this piece of code:
int mode = android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB ? AudioManager.MODE_IN_COMMUNICATION : AudioManager.MODE_IN_CALL;
Now, lets say I run this code on some device that is pre-gingerbread.
Is there any case in which the non-available static import of AudioManager.MODE_IN_COMMUNICATION would be hit?
What I mean is, is there any scenario in which I would see a crash due to the MODE_IN_COMMUNICATION which is not available pre gingerbread being checked?
How does the ternary operator compile in Java? Does it compile these two things as ints? Does it expand the code during compilation?
A static final "variable" that is known at compile time is compiled into your code under certain circumstances. (e.g. every int where the compiler knows the final value)
So your code is actually just
int mode = android.os.Build.VERSION.SDK_INT >= 11 ? 3 : 2;
And any version can run that. It's not making any references to the constants that may or may not exist on the Android device.
The technical details can be found within the Java Language Specifiction e.g. §13.1
References to fields that are constant variables (§4.12.4) are resolved at compile time to the constant value that is denoted. No reference to such a field should be present in the code in a binary file
You can see from the documentation if something is such a constant value.
Build.VERSION_CODES.HONEYCOMB "Constant Value: 11"
AudioManager.MODE_IN_COMMUNICATION "Constant Value: 3"
AudioManager.MODE_IN_CALL "Constant Value: 2"
Build.VERSION.SDK_INT is itself a static final int but is not inlined at compile time. The documentation does not state a constant value
Build.VERSION.SDK_INT
It is implemented as
public static final int SDK_INT = SystemProperties.getInt("ro.build.version.sdk", 0);
and the compile can't figure out what SystemProperties.getInt will return so this value is the only one that actually references a value from within your device.
Scroll to the bottom of my answer to see what the actual javac source code does :)
The Bytecode Generated
#zapl's answer definitely answers the specific question, but I feel like the OP's question still wasn't answered. How exactly does JVM compile ternary operators? So, I just want to answer that question for everyone wondering.
We can figure this out by looking at the actual bytecode generated. So, I created a test where I have two outside classes that have some static variable I'm referencing, and all that, but that's still besides the point because we just want to know if it compiles it the same way as an if-else. Regardless, I did a test with ternary and with the equivalent if-else and these are the results.
Java Code:
class main {
public static void main(String[] args) {
int a = 0;
int b = 2;
int c = a > b ? MyBigClass.VAR_1 : MyOtherBigClass.VAR_2;
//int c;
// if (a > b) {
// c = MyBigClass.VAR_1;
// } else {
// c = MyOtherBigClass.VAR_2;
// }
}
}
class MyBigClass {
public static int VAR_1 = 0;
}
class MyOtherBigClass {
public static int VAR_2 = 1;
}
As you can see I commented out the if-else for the test with the ternary, and then I just commented out the ternary when I was testing the if-else. The bytecode that resulted was this.
Bytecode using if-else:
public static void main(java.lang.String[]);
Code:
0: iconst_0
1: istore_1
2: iconst_2
3: istore_2
4: iload_1
5: iload_2
6: if_icmple 16
9: getstatic #2 // Field MyBigClass.VAR_1:I
12: istore_3
13: goto 20
16: getstatic #3 // Field MyOtherBigClass.VAR_2:I
19: istore_3
20: return
Bytecode using the ternary:
public static void main(java.lang.String[]);
Code:
0: iconst_0
1: istore_1
2: iconst_2
3: istore_2
4: iload_1
5: iload_2
6: if_icmple 15
9: getstatic #2 // Field MyBigClass.VAR_1:I
12: goto 18
15: getstatic #3 // Field MyOtherBigClass.VAR_2:I
18: istore_3
19: return
The resulting bytecode literally has just one extra instruction, which is storing the result in the first branch of the if-statement (wherease the ternary just stores the result at the end of the comparison). So, ternaries only execute the branch that would be followed according to the evaluation of the argument, just like an if-else statement.
And, because I was curious, I decided to check if a double ternary is equivalent to an if-elif-else. (Spoiler it is). I used this code to test it, same process as above:
public static void main(String[] args) {
int a = 0;
int b = 2;
int c = 3;
int d = a > b ? MyBigClass.VAR_1 : a > c ? MyOtherBigClass.VAR_2 : 0;
// int d;
// if (a > b) {
// d = MyBigClass.VAR_1;
// } else if (a > c) {
// d = MyOtherBigClass.VAR_2;
// } else {
// d = 0;
// }
}
The bytecode generated for if-elif-else:
public static void main(java.lang.String[]);
Code:
0: iconst_0
1: istore_1
2: iconst_2
3: istore_2
4: iconst_3
5: istore_3
6: iload_1
7: iload_2
8: if_icmple 19
11: getstatic #2 // Field MyBigClass.VAR_1:I
14: istore 4
16: goto 35
19: iload_1
20: iload_3
21: if_icmple 32
24: getstatic #3 // Field MyOtherBigClass.VAR_2:I
27: istore 4
29: goto 35
32: iconst_0
33: istore 4
35: return
The bytecode generated for ternary:
public static void main(java.lang.String[]);
Code:
0: iconst_0
1: istore_1
2: iconst_2
3: istore_2
4: iconst_3
5: istore_3
6: iload_1
7: iload_2
8: if_icmple 17
11: getstatic #2 // Field MyBigClass.VAR_1:I
14: goto 29
17: iload_1
18: iload_3
19: if_icmple 28
22: getstatic #3 // Field MyOtherBigClass.VAR_2:I
25: goto 29
28: iconst_0
29: istore 4
31: return
What Javac Source Code Actually Does
For the bold the brave and the few
I decided to look at the source for javac... It took awhile, but with a little help from their hitchhiker's guide to javac, I was able to find the one line that definitively determines what happens. Check it out (Line 914): https://hg.openjdk.java.net/jdk9/jdk9/langtools/file/65bfdabaab9c/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java
Do you see that? Let me clarify this a bit, lines 905-918 say this:
/** Expression1Rest = ["?" Expression ":" Expression1]
*/
JCExpression term1Rest(JCExpression t) {
if (token.kind == QUES) {
int pos = token.pos;
nextToken();
JCExpression t1 = term();
accept(COLON);
JCExpression t2 = term1();
return F.at(pos).Conditional(t, t1, t2);
} else {
return t;
}
}
The comment tells us this is what they use for parsing ternary expressions, and if we look at what it returns, it returns a conditional where t is the expression being evaluated, t1 is the first branch, and t2 is the second branch. Let's take a look at Conditional just to be sure. It looks like Conditional is being called from F, which if we dig a little deeper we can find out is the TreeMaker, what is a tree maker you may ask? Well, it's specifically an Abstract Syntax Tree which is often used as an intermediate representation of the code being parsed (check it out here https://en.wikipedia.org/wiki/Abstract_syntax_tree). Anyways, if we look inside that file (https://hg.openjdk.java.net/jdk9/jdk9/langtools/file/65bfdabaab9c/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java) we can see at lines 306-313 this:
public JCConditional Conditional(JCExpression cond,
JCExpression thenpart,
JCExpression elsepart)
{
JCConditional tree = new JCConditional(cond, thenpart, elsepart);
tree.pos = pos;
return tree;
}
Which further confirms exactly what we thought, that a ternary expression is compiled exactly the same as an if-else statement (otherwise known as a conditional statement) :) I encourage anyone interested to take a look at the hitchhiker's guide (https://openjdk.java.net/groups/compiler/doc/hhgtjavac/index.html) and the code, it's actually really interesting to see how even a commercial grade compiler follows a lot of the principle things that you learn about in your standard compiler course at college.
The Java compiler literally replaces it with an if else block. I remember reading about this in a book during Programming Fundamentals I or II.
return isValid ? foo : bar;
literally precompiles to
if(isValid) {
return foo;
} else {
return bar;
}
which is then compiled as normal.
I have here two functions. They all have the same purposes, but they do not have the same parameter setup. Which one is faster? Thanks in advance.
//This function's purpose is to print x + y times.
//total = x + y
public void function(int total){
for (int i = 0; i < total; i++){
System.out.println("Hello.");
}
}
//This function is the same as the above.
//The value is calculated within a "for" loop
public void function(int x, int y){
for (int i = 0; i < x + y; i++){
System.out.println("Hello.");
}
}
public void main(String[] arg){
//Initial variables.
int x = 4;
int y = 6;
//First function call
function(x + y);
//Second function call
function(x, y);
}
I would actually expect javac to optimize out the summation of x + y but it didn't. So this would leave things to other improvements on runtime like JIT or AOT.
Now thing is where would you use this Java code? On a desktop machine, server, Android mobile, Java card? If you don't have such concern then answer shouldn't be really important to you. However if you are targeting Java cards, then of course you should use the better one.
(Below is from javac / javap 1.7.0_09)
public void function(int);
Code:
0: iconst_0
1: istore_2
2: iload_2
3: iload_1
4: if_icmpge 21
7: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
10: ldc #3 // String Hello.
12: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
15: iinc 2, 1
18: goto 2
21: return
public void function(int, int);
Code:
0: iconst_0
1: istore_3
2: iload_3
3: iload_1
4: iload_2
5: iadd
6: if_icmpge 23
9: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
12: ldc #3 // String Hello.
14: invokevirtual #4 // Method java/io/PrintStream.println(Ljava/lang/String;)V
17: iinc 3, 1
20: goto 2
23: return
One call to System.out.println is at least three orders of magnitude costlier than primitive add operations, so writing a function that prints something in a loop and worrying about int addition makes no sense. You could be approximating a sine function with Taylor series expansion up to the 100th member, and it would still be unlikely that you'd see a difference between the two versions of your function.
The main point is, even the most experienced Java veterans don't trust their instinct when it comes to performance. Always profile your code before even starting to think about bottlenecks.
This question already has answers here:
Is there a performance difference between a for loop and a for-each loop?
(16 answers)
Closed 5 years ago.
In Java, is it faster to iterate through an array the old-fashioned way,
for (int i = 0; i < a.length; i++)
f(a[i]);
Or using the more concise form,
for (Foo foo : a)
f(foo);
For an ArrayList, is the answer the same?
Of course for the vast bulk of application code, the answer is it makes no discernible difference so the more concise form should be used for readability. However the context I'm looking at is heavy duty technical computation, with operations that must be performed billions of times, so even a tiny speed difference could end up being significant.
If you're looping through an array, it shouldn't matter - the enhanced for loop uses array accesses anyway.
For example, consider this code:
public static void main(String[] args)
{
for (String x : args)
{
System.out.println(x);
}
}
When decompiled with javap -c Test we get (for the main method):
public static void main(java.lang.String[]);
Code:
0: aload_0
1: astore_1
2: aload_1
3: arraylength
4: istore_2
5: iconst_0
6: istore_3
7: iload_3
8: iload_2
9: if_icmpge 31
12: aload_1
13: iload_3
14: aaload
15: astore 4
17: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
20: aload 4
22: invokevirtual #3; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
25: iinc 3, 1
28: goto 7
31: return
Now change it to use an explicit array access:
public static void main(String[] args)
{
for (int i = 0; i < args.length; i++)
{
System.out.println(args[i]);
}
}
This decompiles to:
public static void main(java.lang.String[]);
Code:
0: iconst_0
1: istore_1
2: iload_1
3: aload_0
4: arraylength
5: if_icmpge 23
8: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
11: aload_0
12: iload_1
13: aaload
14: invokevirtual #3; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
17: iinc 1, 1
20: goto 2
23: return
There's a bit more setup code in the enhanced for loop, but they're basically doing the same thing. No iterators are involved. Furthermore, I'd expect them to get JITted to even more similar code.
Suggestion: if you really think it might make a significant difference (which it would only ever do if the body of the loop is absolutely miniscule) then you should benchmark it with your real application. That's the only situation which matters.
This falls squarely in the arena of micro-optimization. It really doesn't matter. Stylistically I always prefer the second because it's more concise, unless you need the loop counter for something else. And that's far more important than this kind of micro-optimization: readability.
That being said, For an ArrayList there won't be much difference but a LinkedList will be much more efficient with the second.
Measure it. The answer on all performance-questions can depend on VM-version, processor, memory-speed, caches etc. So you have to measure it for your particular platform.
Personally I would prefer the second variant, because the intention is more clear. If performance becomes a problem I can optimize it later anyways - if that code really is important for the performance of the whole application.
For a LinkedList:
for(ClassOfElement element : listOfElements) {
System.out.println(element.getValue());
}
It was answered before:
Is there a performance difference between a for loop and a for-each loop?
On an array, or RandomAccess collection you can get a tiny increase in speed by doing:
List<Object> list = new ArrayList<Object>();
for (int i=0, d=list.size(); i<d; i++) {
something(list.get(i));
}
But I wouldn't worry in general. Optimisations like this wont make more than 0.1% difference to your code. Try invoking java with -prof to see where your code is actually spending its time.
Even faster is to use the ParallelArray of the fork-join framework (if you have large enough dataset).