0

I am currently working with a lot of nested level objects and was thinking about performance.

So let's say I have the following classes:

class Address {
    private String doorNumber;
    private String street;
    ...
}

and another class Customer.

class Customer {
    private List<Address> addressList;
    private String firstName;
    .....
    .....
    .....
}

and when I try to access it like below:

public static void main(String[] str) {
    Customer customer = new Customer();
    // ... and add addresses to this Customer object.

    // Set 1
    // And if I then do...
    customer.getAddressList().get(0).getHouseNumber();
    customer.getAddressList().get(0).getStreet();

    // OR
    // Set 2
    Address address = customer.getAddressList().get(0);
    address.getHouseNumber();
    address.getStreet()
}

I know the first set of lines to access the Address is not clean code, and I assumed the compiler would sort this out but it doesn't. Because when I decompile my code, I get exactly the same thing back so am not sure if the compiler is doing any optimisations there. So my first question is why doesn't the compiler clean this up and assign it to a temporary variable?

And my next question is, is this something to do with performance? And which is the more optimal performant code of the two, apart from the first not being very clean code. Does it mean, that my 2nd set of lines of code would internally get translated to the first during compilation?

And finally the last one, is it more optimal to call variables on a class than its getter method? Am just thinking performance here without clean coding.

Vishal Jumani
  • 177
  • 11
  • The second version will perform better, especially if `addressList` is a `LinkedList<>` or some other `List<>` where `get` is not `O(1)`. (Assuming you will dig deeper than the first element.) – bradimus Nov 16 '16 at 15:45
  • Thanks @bradimus. The idea is that do I have to be diligent about variable assignment or does the Java compiler do any optimisations? Am not sure. – Vishal Jumani Nov 16 '16 at 15:48
  • You can check the performance by yourself by benchmarking it. Run the code a few thoudsand times in a loop and measure time with `System.nanoTime()`. – André Stannek Nov 16 '16 at 15:48
  • If you are going to benchmark, make sure you do it correctly: http://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in-java – bradimus Nov 16 '16 at 15:50
  • Thanks @AndréStannek. Performance could vary on different implementations of the JVM. And I probably don't have access to the different types of JVM implementations. I was merely trying to see if anyone had an idea about this or the internal workings of the compiler. Just food for thought really? Does that make sense? – Vishal Jumani Nov 16 '16 at 15:51
  • @VishalJumani if you need a represantative benchmark, e.g. to make scallability predictions, this is true. You also should consider bradimus comment then. But if you just want an idea of the scale or difference between two approaches, imho a simple benchmark like I described is sufficient. – André Stannek Nov 16 '16 at 15:56
  • Thanks @AndréStannek. Yeah, sorry. Will keep that in mind for next time.. – Vishal Jumani Nov 16 '16 at 16:00

1 Answers1

5

Side effects.

Consider this case, where instead of returning some text, calling your get method has some internal side effect:

// This goes up each time getAddressList is called.
public int addressesRequested;

public List<Address> getAddressList(){
    addressesRequested++;
    return addressList;
}

Of course, in this method such a side effect doesn't make much sense, but there are a wide variety of ways in which a method call can leave some form of left over effect.

customer.getAddressList(); // addressesRequested is now 1.
customer.getAddressList(); // addressesRequested is now 2.
...

As a result, the compiler can't optimise multiple method calls into one - it has to assume that a method call has side effects.

It's also worth noting that a method can also be inlined - that's where the body of the method is copied to the call site to avoid a method calls overhead. This generally only happens when the JVM believes such an optimization is merited; i.e. because it's being called frequently. It does not, however, result in the callsite being optimised any further - it won't trigger some kind of temporary variable there.

What about fields? They can't produce side effects..can they?

Ok, so you're now thinking about this:

// Assume addressList was public and could be accessed like so:
customer.addressList.get(0)..
customer.addressList.get(0)..
..

They don't produce side effects, but the compiler won't drop it in a temporary variable either. This is because side effects are a two way street - some other method could be changing that addressList field; most likely from some other thread.

Luke Briggs
  • 3,745
  • 1
  • 14
  • 26
  • Love your answer Luke, that makes so much sense now. I don't know why I didn't think about it from that perspective and I kept thinking getters and setters. – Vishal Jumani Nov 16 '16 at 15:53
  • Yup, I was more thinking on the lines of the inline substitution when I wrote the question – Vishal Jumani Nov 16 '16 at 15:57
  • @VishalJumani No problem! I had a feeling inlining was probably being considered (getters/ setters are usually the first kinds of methods to get inlined, because of how frequently they get used) so it seemed worthwhile to drop that extra note in there too. – Luke Briggs Nov 16 '16 at 15:59
  • @VishalJumani a follow up thought is also *"how about fields? They don't produce side effects?"* but the assumption applies to them too - I'll add something about that too if you'd like. – Luke Briggs Nov 16 '16 at 16:10