Java 10: Byte Code Generation for Enhanced For Loops [duplicate] - java

This question already has answers here:
Java "for" statement implementation prevents garbage collecting
(6 answers)
Closed 4 years ago.
The following example describes the generation of the following lines of code until Java 9.
List data = new ArrayList<>();for (String b : data);
public class Test
{
public Test() {}
public static void main(String[] paramArrayOfString) throws IOException {
ArrayList localArrayList = new ArrayList();
String str;
for (Iterator localIterator = localArrayList.iterator(); localIterator.hasNext(); str = (String)localIterator.next()) {}
}
In Java 10, iterator variables are declared outside for loops and initialized to the null value immediately once the operation is over, so GC can get rid of unused memory.
{
Iterator iterator = data.iterator();
for (; iterator.hasNext();)
{
String b = (String)iterator.next();
}
b = null;
iterator = null;
}
How is setting reference null explicitly better than reference going out of scope by the end of the for loop.
Source: https://dzone.com/articles/features-in-java-10
Also, adding link from the comments : https://bugs.openjdk.java.net/browse/JDK-8192858

Edit: There already exists a related question: Java "for" statement implementation prevents garbage collecting that provides more information.
After reading through the bug-reports (https://bugs.openjdk.java.net/browse/JDK-8192858 and https://bugs.openjdk.java.net/browse/JDK-8175883) the reason for this change can be summarised as the following:
There was an issue in the bytecode produced by javac which resulted in a reference to an array / iterable being kept after the completion of the loop.
As a result, even if an array / iterable is explicitly nullified, there is still a reference to the array / iterable which means that the array / iterable was not eligible for garbage collection until leaving the scope of the method.
With large arrays / iterables (as per the example below) this could result in an OutOfMemoryError.
This is demonstrated by this use case here (taken from the bug report):
public class IteratorInOneScope {
private static final int HALF_OF_MEMORY = (int) (Runtime.getRuntime().maxMemory() * 0.5);
public static void main(String[] args) {
byte[] data = new byte[HALF_OF_MEMORY];
for (byte b : data); // <-- if you comment this line - the application finished successfully
data = null; // this expects to discard reference -> allow to release the memory
byte[] data2 = new byte[HALF_OF_MEMORY]; // the memory can't be allocated second time, if the "for" loop statement above is used
System.out.println("Success");
}
}
Which compiled to the following bytecode:
0: getstatic #2 // Field HALF_OF_MEMORY:I
3: newarray byte
5: astore_0 <==== array ref in slot #0
6: aload_0
7: astore_1 <==== array ref in slot #1
8: aload_1
9: arraylength
10: istore_2
11: iconst_0
12: istore_3
13: iload_3
14: iload_2
15: if_icmpge 29
18: aload_1
19: iload_3
20: baload
21: istore 4
23: iinc 3, 1
26: goto 13
29: aconst_null
30: astore_0 <== nulls slot #0
31: getstatic #2 // Field HALF_OF_MEMORY:I
34: newarray byte
36: astore_1
37: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
40: ldc #4 // String Success
42: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
45: return
There is no update to JLS 14.14.2, for this reason:
JLS 14.14.2 is only concerned with semantics of the statement, not
garbage collection behavior. Compilers are free to generate whatever
bytecode they want that produces the specified behavior. So, if javac
wants to set some unused locals to null, it's free to do so. No spec
change necessary, and it would be a mistake to include it in the spec,
because it doesn't impact the statement's semantics.

Related

Why do instance variables have default values in java? [duplicate]

This question already has answers here:
Uninitialized class members in Java do not issue any compiler errors. local variables however do. Why?
(5 answers)
Closed 6 years ago.
Why do variables declared in a class have default values, but the variables declared inside methods, said to be "local variables", don't have default values in Java?
For example
class abc
{
int a;
public static void main(String ss[])
{
int b;
abc aa=new abc();
System.out.println(aa.a);
System.out.println(b);
}
}
In this above example variable a has default value of 0 but variable b gives error that it might not have been initialized.
All member variable have to load into heap so they have to initialized with default values when an instance of class is created. In case of local variables, they don't get loaded into heap they are stored in stack until they are being used before java 7, so we need to explicitly initialize them.
Now the "Java Hotspot Server Compiler" performs "escape analysis" and decides to allocate some variables on the stack instead of the heap.
Local variables Initialization
Variables declared in methods and in blocks are called local variables. Local variable are not initialized when they are created at method invocation. Therefore, a local variable must be initialized explicitly before being used. Otherwise the compiler will flag it as error when the containing method or block is executed.
Example:
public class SomeClassName{
public static void main(String args[]){
int total;
System.out.println("The incremented total is " + total + 3); //(1)
}
}
The compiler complains that the local variable total used in println statement at (1) may not be initialized.
Initializing the local variable total before usage solves the problem:
public class SomeClassName{
public static void main(String args[]){
int total = 45; //Local variable initialized with value 45 System.out.println("The incremented total is " + total+ 3); //(1)
}
}
Fields initialization
If no initialization is provided for an instance or static variable, either when declared or in an initializer block, then it is implicitly initialized with the default value of its type.
An instance variable is initialized with the default value of its type each time the class is instantiated, that is for every object created from the class.
A static variable is initialized with the default value of its type when the class is first loaded.
As local variables are allocated on stack, memory chunk for a local variable is allocated when it is assigned with a value.
Take simple example
class Abc {
int i = -111;
int e;
int doSomething() {
int a = 10;
int b = a + i;
int c = b + 100;
Abc d = new Abc();
e = b + c + d.a;
return e + 1000;
}
}
and the bytecode from javap -c Abc
Compiled from "Abc.java"
class Abc {
int i;
int e;
Abc();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: bipush -111
7: putfield #2 // Field i:I
10: return
int doSomething();
Code:
0: bipush 10
2: istore_1
3: iload_1
4: aload_0
5: getfield #2 // Field i:I
8: iadd
9: istore_2
10: iload_2
11: bipush 100
13: iadd
14: istore_3
15: new #3 // class Abc
18: dup
19: invokespecial #4 // Method "<init>":()V
22: astore 4
24: aload_0
25: iload_2
26: iload_3
27: iadd
28: aload 4
30: getfield #2 // Field i:I
33: iadd
34: putfield #5 // Field e:I
37: aload_0
38: getfield #5 // Field e:I
41: sipush 1000
44: iadd
45: ireturn
}
When a method is inovked a memory space in the stack called current frame is allocated
If you look carefully even int a=-111; assignment happens in an implicit init function Abc() !
int a = -111;
5: bipush -111
7: putfield #2 // Field a:I
As field variable e is not assigned any value it will be 0 if primitive or null if a Object reference
And if you look at doSomething()
int a = 10;
0: bipush 10
for a local to be used the initial value needs to be pushed into stack in this case 10 . without this 'push' [initialization] a's value is not accessible to subsequent statements (as the value is not on the stack). once the value is pushed to stack other operations like iadd istore etc are carried out on the stack
below statement actually creates an object on the heap space and invokes init method. This is where un initialized variables like 'e' gets default values
15: new #3 // class Abc
18: dup
I leave further bytecode comparison upto you ;) but I hope it is clear
tl;dr: It was more or less an arbitrary choice
If you ask me, it was a mistake that Java has default values for instance variables. The compiler should have forced the programmer to initialize it before like it is the case for local variables.
The rationale behind the default values is safety. When an object is instantiated, a chunk of memory will be allocated for the object which contains where the instance variables are pointing to etc. The Java designers decided it would be a good idea to wipe this part of memory with zeros and nulls. This way you will never read garbage that happened to be there before the object was allocated. They could have forced initialization; there is nothing fundamental about the choice. It probably made things easy to implement and made enough sense to the designers of Java.
In case of local variables, the designers chose to force initialization (or perhaps it's more accurate to say they chose to not do any kind of initialization when a local variable is only declared, and thus the most logical behavior of the compiler was to force initialization of the variable before use).

Using Generics on right hand side in Java 6?

I java 6 i can declare the arraylist as follows
Way1: using generics i.e <Integer> on right hand side too
List<Integer> p = new ArrayList<Integer>();
Way2: using the diamond operator
List<Integer> p = new ArrayList<>();
Way3: using generic only at left side
List<Integer> p = new ArrayList();
I prefer to use way 3 as its brief. Is there any difference between these ways? Which one we should prefer and why?
Update:- I know in java 7 second way is recommended but my question is in context of java 6. Which one is preferable?
To me, way 3 also says p is an arraylist of integers (same conveyed by way1). So I find no difference except the fact IDE displays warning message:
ArrayList is a raw type. References to generic type ArrayList<E> should be parameterized
As has been pointed out, Way 2 isn't valid in 1.6. So the question is, is there any difference between Way 1 and Way 3. Apart from readability, no.
Take this code:
import java.util.*;
class G {
public static void main(String [] args){
List<Integer> listOne = new ArrayList<Integer>();
listOne.add(1);
int one = listOne.get(0);
List<Integer> listTwo = new ArrayList();
listTwo.add(1);
int two = listTwo.get(0);
}
}
Compile it and look at the bytecode using javap -c
public static void main(java.lang.String[]);
Code:
// FOR listOne
0: new #2 // class java/util/ArrayList
3: dup
4: invokespecial #3 // Method java/util/ArrayList."<init>":()V
7: astore_1
8: aload_1
9: iconst_1
10: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
13: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
18: pop
19: aload_1
20: iconst_0
21: invokeinterface #6, 2 // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
26: checkcast #7 // class java/lang/Integer
29: invokevirtual #8 // Method java/lang/Integer.intValue:()I
32: istore_2
// FOR listTwo
33: new #2 // class java/util/ArrayList
36: dup
37: invokespecial #3 // Method java/util/ArrayList."<init>":()V
40: astore_3
41: aload_3
42: iconst_1
43: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
46: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
51: pop
52: aload_3
53: iconst_0
54: invokeinterface #6, 2 // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
59: checkcast #7 // class java/lang/Integer
62: invokevirtual #8 // Method java/lang/Integer.intValue:()I
65: istore 4
67: return
}
We can see that the exact same bytecode is produced in both cases. Note that as Generics aren't baked in the compiler throws away the information after checking it at compile time and adds in checkcast instructions to make sure the casts it does when retrieving objects are safe.
The second way is not possible in Java 6. It is the new way of inferring Generic instance in Java 7.
There is no difference, if you are using java 7 prefer the second method, but is it not available in java 6. It is a new addition to java 7.
Both are same but way2 is available from java 7
Both are same .But there are version difference in both of this.
But second way is not possible in java 6.In Java 7 if we not declare type in right side then by default it will take same type as left side.
http://docs.oracle.com/javase/7/docs/technotes/guides/language/type-inference-generic-instance-creation.html
So as per your updates, if you have to use Java 6,then you should use way1.
Way 3 uses raw types. You should never use raw types. They should only be used in legacy code.
Way 3 is not good. Mixing Generics and raw types is naughty, as you are making an assumption for runtime about types, and can run into ClassCastExceptions like the following code:
ArrayList b = new ArrayList();
b.add(5);
ArrayList<String> a = new ArrayList(b);
System.out.println(a.get(0));
So for Java 6, always use way 1

what is the advantage using iterator? [duplicate]

This question already has answers here:
Which is more efficient, a for-each loop, or an iterator?
(7 answers)
Closed 9 years ago.
for(Element e : elementList)
for (Iterator<Element> itr = elementList.iterator(); itr.hasNext();)
First one is much simpler. Are there any advantages or reasons that I want to do the second one?
Internally both of them use iterator, the only difference is that code is more clear and shorter when you used enhanced for loop. Here is what javadoc says about both:
Iterating over a collection is uglier than it needs to be. Consider the following method, which takes a collection of timer tasks and cancels them:
void cancelAll(Collection<TimerTask> c) {
for (Iterator<TimerTask> i = c.iterator(); i.hasNext(); )
i.next().cancel();
}
The iterator is just clutter. Furthermore, it is an opportunity for error. The iterator variable occurs three times in each loop: that is two chances to get it wrong. The for-each construct gets rid of the clutter and the opportunity for error. Here is how the example looks with the for-each construct:
void cancelAll(Collection<TimerTask> c) {
for (TimerTask t : c)
t.cancel();
}
When you see the colon (:) read it as “in.” The loop above reads as “for each TimerTask t in c.” As you can see, the for-each construct combines beautifully with generics. It preserves all of the type safety, while removing the remaining clutter. Because you don't have to declare the iterator, you don't have to provide a generic declaration for it. (The compiler does this for you behind your back, but you need not concern yourself with it.)
For complete description about why we should use for-each loop and not iterator, read this:
http://docs.oracle.com/javase/1.5.0/docs/guide/language/foreach.html
The first form was introduced in Java 5, and the second form is mostly found in legacy code from earlier versions of the language. Nonetheless, there are a few situations where you still need to use the second form; for example, if the loop needs to be able to remove some (or all) of the elements as they're iterated over, then you need to have itr so you can call its remove method.
Iterator can remove() the element from the collection , which cannot be done using for each loop
There is No performance difference. But by using iterator you will have more functionalities to be used. For example you can reference the Iterator in your loop. This allows you to do things like remove collection items getting a ConcurrentModificationException.
You can use following
for (Iterator<Element> itr = elementList.iterator(); itr.hasNext();){
if(o meets some condition){
itr.remove();
}
}
but not this
for(Element e : elementList){
if(o meets some condition){
elementList.remove(e);
}
}
But if this difference doesn't bother you then you can use the one that comforts your.
similar so question
They are very much the same. Consider this code
import java.util.Iterator;
import java.util.ArrayList;
public class IteratorTest {
public static void main(String[] args){
ArrayList<Object> list = new ArrayList();
list.add(new Object());
list.add(new Object());
for(Object o : list)
System.out.println(o);
for(Iterator<Object> itr = list.iterator();itr.hasNext();)
System.out.println(itr.next());
}
}
We then compile it and disassemble it using
javap -c IteratorTest
and get the following bytecode for the main method
public static void main(java.lang.String[]);
Code:
0: new #2 // class java/util/ArrayList
3: dup
4: invokespecial #3 // Method java/util/ArrayList."<init>":()V
7: astore_1
8: aload_1
9: new #4 // class java/lang/Object
12: dup
13: invokespecial #1 // Method java/lang/Object."<init>":()V
16: invokevirtual #5 // Method java/util/ArrayList.add:(Ljava/lang/Object;)Z
19: pop
20: aload_1
21: new #4 // class java/lang/Object
24: dup
25: invokespecial #1 // Method java/lang/Object."<init>":()V
28: invokevirtual #5 // Method java/util/ArrayList.add:(Ljava/lang/Object;)Z
31: pop
32: aload_1
33: invokevirtual #6 // Method java/util/ArrayList.iterator:()Ljava/util/Iterator;
36: astore_2
37: aload_2
38: invokeinterface #7, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z
43: ifeq 63
46: aload_2
47: invokeinterface #8, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
52: astore_3
53: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream;
56: aload_3
57: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
60: goto 37
63: aload_1
64: invokevirtual #11 // Method java/util/ArrayList.iterator:()Ljava/util/Iterator;
67: astore_2
68: aload_2
69: invokeinterface #7, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z
74: ifeq 92
77: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream;
80: aload_2
81: invokeinterface #8, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
86: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
89: goto 68
92: return
}
Lines 32 to 60 are the first loop and lines 63 to 89 are the second loop. You will notice that they're pretty much the same - just with the names of some locals changed and slight reordering.
Therefore, as the compiler produces the same bytecode for the two expressions they are the same.

Does the Java foreach loop create a new object?

Will Java create a new object each time a foreach loop is entered?
I'm not talking about each iteration, but if you have a foreach loop that is used multiple times, is it creating objects each time?
Simple Example:
for(Object o : Objects)
{
for(Object p : Objects2)
{
}
}
Would there only be one p per execution, or would p be instantiated for each Object o?
Does the Garbage Collector have to recover an object from the foreach loop each time it is exited?
More specifically, I'm writing some Android based game code. It will iterate over all the game objects at a given rate per second, or as fast as it can depending on the circumstances.
It may be a case of premature optimization, but if using an explicit for or while loop can guarantee me that I won't have excess garbage collection from my loops, then I can establish that as a coding standard for the project.
More specifically:
public void update()
{
for(GameObject gObj : gameObjects)
{
gObj.update();
}
}
With update() being called from a thread designed to make the calls based on the timing I described before.
Update:
I am asking if there is a new Reference p being created for each o in Objects. Not if it copies the objects in Objects2. So does the VM have to create a new Reference p, and then does it collect that reference between iterations of the outter loop? And more specifically in my case, does it collect the reference between method calls?
Update:
From comments on Matt Ball's answer.
Which would create less Garbage Collection work?
//Loop just to have it run a number of times
//Could be running the inner foreach numerous time for any reason
for(int i = 0; i < 1000; i++)
{
for(Object o : Objects)
{
o.update();
}
}
vs.
Iterator<Object> iter;
//Loop just to have it run a number of times
//Could be running the inner foreach numerous time for any reason
for(int i = 0; i < 1000; i++)
{
iter = Objects.iterator();
while(iter.hasNext());
{
iter.getNext().update();
}
}
Update:
Comparing scopes:
import java.util.ArrayList;
import java.util.Iterator;
public class TestLoops
{
public static Iterator<Object> it;
public static ArrayList<Object> objects;
public static void main(String... args)
{
objects = new ArrayList<Object>();
it = objects.iterator();
it = objects.iterator();
//Every time we start a foreach loop, does it creates a new reference?
Iterator<Object> newIt1 = objects.iterator();
Iterator<Object> newIt2 = objects.iterator();
}
}
Produces this Bytecode:
public class TestLoops {
public static java.util.Iterator<java.lang.Object> it;
public static java.util.ArrayList<java.lang.Object> objects;
public TestLoops();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String...);
Code:
0: new #2 // class java/util/ArrayList
3: dup
4: invokespecial #3 // Method java/util/ArrayList."<init>":()V
7: putstatic #4 // Field objects:Ljava/util/ArrayList;
10: getstatic #4 // Field objects:Ljava/util/ArrayList;
13: invokevirtual #5 // Method java/util/ArrayList.iterator:()Ljava/util/Iterator;
16: putstatic #6 // Field it:Ljava/util/Iterator;
19: getstatic #4 // Field objects:Ljava/util/ArrayList;
22: invokevirtual #5 // Method java/util/ArrayList.iterator:()Ljava/util/Iterator;
25: putstatic #6 // Field it:Ljava/util/Iterator;
28: getstatic #4 // Field objects:Ljava/util/ArrayList;
31: invokevirtual #5 // Method java/util/ArrayList.iterator:()Ljava/util/Iterator;
34: astore_1
35: getstatic #4 // Field objects:Ljava/util/ArrayList;
38: invokevirtual #5 // Method java/util/ArrayList.iterator:()Ljava/util/Iterator;
41: astore_2
42: return
}
There is no magical object construction that happens with a for-each loop. This syntax:
for(Object o : Objects)
{
for(Object p : Objects2)
{
}
}
is only shorthand for this:
for(Iterator<Object> iter = Objects.iterator(); iter.hasNext();)
{
Object o = iter.next();
for(Iterator<Object> iter2 = Objects2.iterator(); iter2.hasNext();)
{
Object p = iter2.next();
}
}
If would there be an iter2 reference created for every object I had in Objects?
That depends on where Objects2 comes from. Does it come from o? It also depends on how Objects2.iterator() is implemented. Iterable#iterator() is expected to return independent iterators, which means that Objects2.iterator() would almost certainly return a new Iterator for each invocation. But that does not say anything about whether or not iterating through the objects in Objects2 (using iter2) creates other new objects.
This sounds like premature optimization, overall.
Which would create less Garbage Collection work?
Neither. The enhanced for loop (aka for-each loop) is simply syntactic sugar. The bytecode produced by the compiler is identical. Complete example:
package com.stackoverflow;
import java.util.Iterator;
public class Question14640184
{
public void enhancedForLoop(Iterable<Object> objects1, Iterable<Object> objects2)
{
for(Object o1 : objects1)
{
for(Object o2 : objects2)
{
// do something
}
}
}
public void iteratorForLoop(Iterable<Object> objects1, Iterable<Object> objects2)
{
for(Iterator<Object> iter1 = objects1.iterator(); iter1.hasNext();)
{
Object o1 = iter1.next();
for(Iterator<Object> iter2 = objects2.iterator(); iter2.hasNext();)
{
Object o2 = iter2.next();
}
}
}
}
Compile:
✗ javac Question14640184.java
✗ javap -c Question14640184
Compiled from "Question14640184.java"
public class com.stackoverflow.Question14640184 extends java.lang.Object{
public com.stackoverflow.Question14640184();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public void enhancedForLoop(java.lang.Iterable, java.lang.Iterable);
Code:
0: aload_1
1: invokeinterface #2, 1; //InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
6: astore_3
7: aload_3
8: invokeinterface #3, 1; //InterfaceMethod java/util/Iterator.hasNext:()Z
13: ifeq 57
16: aload_3
17: invokeinterface #4, 1; //InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
22: astore 4
24: aload_2
25: invokeinterface #2, 1; //InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
30: astore 5
32: aload 5
34: invokeinterface #3, 1; //InterfaceMethod java/util/Iterator.hasNext:()Z
39: ifeq 54
42: aload 5
44: invokeinterface #4, 1; //InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
49: astore 6
51: goto 32
54: goto 7
57: return
public void iteratorForLoop(java.lang.Iterable, java.lang.Iterable);
Code:
0: aload_1
1: invokeinterface #2, 1; //InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
6: astore_3
7: aload_3
8: invokeinterface #3, 1; //InterfaceMethod java/util/Iterator.hasNext:()Z
13: ifeq 57
16: aload_3
17: invokeinterface #4, 1; //InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
22: astore 4
24: aload_2
25: invokeinterface #2, 1; //InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
30: astore 5
32: aload 5
34: invokeinterface #3, 1; //InterfaceMethod java/util/Iterator.hasNext:()Z
39:
So like I said, the for-each loop is only syntactic sugar.
it's something I'm just fixated on.
Move on, friendo.
It depends entirely on what the Iterable returns. If it creates a new object, so be it. If not, not. In the case of an array, for example, the elements already exist, so no object is created. Same applies to most Collections.
The one case where it almost certainly creates a new object is where the Iterable itself is the result of a function call, for example:
for (Object o : xyz.getIterable())
But this isn't a property of the for-each loop but of the function call.

Nested get performance

In Java, is there a performance hit if i continually use nested get to retrieve values? For instance:
String firstname = getOffice().getDepartment().getEmployee().getFirstName();
String lastname = getOffice().getDepartment().getEmployee().getLastName();
String address = getOffice().getDepartment().getEmployee().getAddress();
VS:
Employee e = getOffice().getDepartment().getEmployee();
String firstname = e.getFirstName();
String lastname = e.getLastName();
String address = e.getAddress();
Would the 2nd version be faster because it has less 'jumps'?
It depends entirely on what the getXYZ calls do. If they're simple accessors to an underlying field, then no, not on HotSpot (Oracle's JVM), because they'll get optimized out if there's any need to do so. If, on the other hand, they do any kind of complex work (traversing a btree, etc.), then of course they'll have to do that work repeatedly (unless HotSpot can prove to itself that the calls are idempotent, which if the code has any complexity becomes unlikely). (Whether it matters that they do the work repeatedly is another question; until/unless you see an actual performance problem, don't worry about it.)
But the second is much more readable and maintainable. That's the more powerful reason for using it.
Rather than performance I see second as better human understandable code. You should not worry about micro optimizations but write a good and clean code.
The optimization you are thinking about is called premature optimization. You should not think about these unless you really have to.
I agree with #AmitD's answer about being the second one more readable. When chaining method calls like this, you can also write them in the following way -
Employee e = getOffice()
.getDepartment()
.getEmployee();
String firstname = e.getFirstName();
String lastname = e.getLastName();
String address = e.getAddress();
to further improve readability.
Possibly yes. But assuming the getters look like ordinary getters on the inside it will probably be so small that it becomes almost impossible to measure.
Also if you run through this code often enough to make it matter, the magic of the Hotspot compiler will kick in and mangle the byte code, probably again making both variations the same.
In the end it is extremely hard to tell what really will happen. If performance matters for you set up a test. If performance doesn't matter enough to justify the costs of the test ... well then it doesn't matter enough to worry.
Either use byte code analysis or time the two approaches using System.nanoTime. I think second one is faster. here is what I did to conclude this:
I wrote three classes as given below:
public static class A {
public B b = new B();
}
public static class B {
public E e = new E();
}
public static class E {
public String name = "s";
public int age = 1;
}
Then I wrote two simple methods and get their java byte code using javap -c CLASS_NAME.
public static void Test1() {
A a = new A();
String str = a.b.e.name;
int age = a.b.e.age;
}
The byte code of above method is:
public static void Test1();
Code:
// new A();
0: new #15
3: dup
4: invokespecial #17
7: astore_0
8: aload_0
// a.b (it accesses the field and put it on operand stack)
9: getfield #18
// b.e
12: getfield #22
// b.name
15: getfield #28
// pop 'name' from stack
18: astore_1
19: aload_0
// cyle continues
20: getfield #18
23: getfield #22
26: getfield #34
29: istore_2
30: return
You can clearly see at byte code level, each time you try to access field, it put the value of that filed on stack and then this cycle continues. Therefore, a.a1.a2....an would be n instruction if stack would have enough spae to hold all n. And there was no optimisation by compiler this same cycle of called again to access both name and age field.
Now here is the second method:
public static void Test2() {
A a = new A();
E e = a.b.e;
String str = e.name;
int age = e.age;
}
Byte code for above method is:
public static void Test2();
Code:
// new A();
0: new #15
3: dup
4: invokespecial #17
7: astore_0
8: aload_0
// store a.b.e on operand stack once
9: getfield #18
12: getfield #22
15: astore_1
16: aload_1
// get 'name' field
17: getfield #28
20: astore_2
21: aload_1
// get 'age' field
22: getfield #34
25: istore_3
26: return
Above is 4 instruction shorter than previous code as it prevents execution of getfield. So I think this should be faster than previous one.

Categories