I noticed that in the jdk source code and more specifically in the collections framework there is a preference in assigning variables right before reading them in expressions. Is it just a simple preference or is it something more important which I am not aware of? One reason that I can think of is that the variable is used in this expression only.
As I am not used to this style I find it hard to read it. The code is very condensed. Below you can see an example taken from java.util.HashMap.getNode()
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
if ((tab = table) != null && (n = tab.length) > 0 && ...) {
...
}
As already mentioned in the comment: Doug Lea, who is one of the main authors of the collections framework and the concurrency packages, tends to do optimizations that may look confusing (or even counterintuitive) for mere mortals.
A "famous" example here is copying fields to local variables in order to minimize the size of the bytecode, which is actually also done with the
table
field and the localtab
variable in the example that you referred to!For very simple tests, it does not seem to make a difference (referring to the resulting bytecode size) whether the accesses are "inlined" or not. So I tried to create an example that roughly resembles the structures of the
getNode
method that you mentioned: An access to a field that is an array, a length check, an access to a field of one array element...testSeparate
method does separate assignments and checkstestInlined
method uses the assignment-in-if-styletestRepeated
method (as a counterexample) does every access repeatedlyThe code:
And the resulting bytecodes: The
testSeparate
method uses 41 instructions:The
testInlined
method indeed is a tad smaller, with 39 instructionsFinally, the
testRepeated
method uses a whopping 63 instructionsSo it seems that this "obscure" way of writing the queries and assignments can, indeed, save a few bytes of bytecode, and (given the justification in the linked answer about storing fields in local variables) this may have been the reason to use this style.
But...
in any case: After the method has been executed a few times, the JIT will kick in, and the resulting machine code will have "nothing" to do with the original bytecode - and I'm pretty sure that all three versions will actually be compiled to the same machine code in the end.
So the bottom line is: Don't use this style. Instead, just write dumb code that is easy to read and maintain. You'll know when it's your turn to use "optimizations" like these.
I have made a further test, and compared the
testSeparate
andtestInlined
method concerning the actual machine code that is generated by the JIT.I modified the
main
method a bit, to prevent unrealistic over-optimizations or other shortcuts that the JIT might take, but the actual methods have been left unmodified.And as expected: When calling the methods a few thousand times with a hotspot disassembly JVM and
-XX:+UnlockDiagnosticVMOptions -XX:+LogCompilation -XX:+PrintAssembly
, then the actual machine code of both methods is identical.So once more, the JIT does its job pretty well, and the programmer can focus on writing readable code (whatever that means).
I did not test the third method,
testRepeated
, because it is not equivalent to the other methods (and thus, it can not result in the same machine code). This is, by the way, another small advantage of the strategy of storing fields in local variables: It offers a (very limited, but sometimes handy) form of "thread safety": It is made sure that the length of an array (like thetab
array in thegetNode
method ofHashMap
) can not change while the method is being executed.