3

The ternary operator normally just is a subject to philosophical discussions: whether

a=b>5?1:0;

is more readable, faster, cooler to

if(b>5) { a=1; } else {a=0;}

(take or leave the curly braces) I normally don't care. I like my ternary operator. But we had a discussion concerning this piece of code:

BigObject myBigObject=null;
...
do {
   myBigObject=
     myBigObject==null?
     createBigObject():
     myBigObject;
...
} while(manyIteration);

Colleague claimed that this construct will create the myBigObject will be copied every loop (except the first) which will waste precious time and memory and that he found the case where the ternary operator is useless. the only way is:

do {
     if(myBigObject==null)
       myBigObject=createBigObject();
...
} while(manyIteration);

I argued that the clever compiler will see that the object is assigned to itself and will optimize it out.

But who is right?

Joachim Weiß
  • 407
  • 2
  • 12
  • Optimize what out? Assigning `null` to itself? – Tunaki Mar 03 '16 at 10:16
  • Sorry, I forget to mention that the function creatBigObject will return a BigObject – Joachim Weiß Mar 03 '16 at 10:20
  • So the actual question is: are the ternary options executed before the condition is evaluated? Besides that a simple assignment would not copy anything so that wouldn't be a real issue in terms of time and space. - From a design point of view I find the second option easier to read and less verbose. You create an object if it doesn't exist already and otherwise simply do nothing. – Thomas Mar 03 '16 at 10:21
  • Possibly duplicate http://stackoverflow.com/questions/9745389/is-the-ternary-operator-faster-than-an-if-condition – T D Nguyen Mar 03 '16 at 10:22
  • What is your concern? myBigObject is a reference in the first place. This is same size independent of the size of the object referenced. So, yes the second variant ensures assignment is occurring on the first loop only. The first variant could fail to be optimized away into second variant for less smart compilers. – rpy Mar 03 '16 at 10:27
  • Just a side note... When you do `myBigObject = someOtherBigObject;`, you are not copying the entire object. You are only copying the referece to the big object. Hence `myBigObject = someOtherBigObject;` and `mySmallObject = someOtherSmallObject;` "cost" the same (very tiny) amount of execution time. – Alderath Mar 03 '16 at 10:29

4 Answers4

5

The definite answer lies in section 15.25 of the JLS (emphasis mine):

The resulting boolean value is then used to choose either the second or the third operand expression: - If the value of the first operand is true, then the second operand expression is chosen. - If the value of the first operand is false, then the third operand expression is chosen.

The chosen operand expression is then evaluated and the resulting value is converted to the type of the conditional expression as determined by the rules stated below.

This conversion may include boxing or unboxing conversion (§5.1.7, §5.1.8).

The operand expression not chosen is not evaluated for that particular evaluation of the conditional expression.

This means both expressions are not always evaluated: only the one that needs to be is. So actually, none of you are right.

  • You're wrong because it is not the compiler being clever: it is specified by the language itself;
  • Your collegue is wrong because the expression won't be evaluated if need not be.

In the code

myBigObject = myBigObject == null ? createBigObject() : myBigObject;
              ^-----------------^   ^---------------^             
           this is true the 1st time, hence that ^ is evaluated


myBigObject = myBigObject == null ?    createBigObject()     :        myBigObject;
              ^-----------------^                              
           this is false the 2nd time, hence that ^ is NOT evaluated, that ^ is

Notice that what is executed is just assigning myBigObject to itself, which does not create a new object.

Tunaki
  • 132,869
  • 46
  • 340
  • 423
  • You could add the section on assignments since that seems to be an issue as well ("Does the assignment create a copy of the object?" or in orignal words "myBigObject will be copied every loop"). – Thomas Mar 03 '16 at 10:35
3

Colleague claimed that this construct will create the myBigObject will be copied every loop (except the first) which will waste precious time and memory and that he found the case where the ternary operator is useless.

You should note that myBigObject is a reference to an object. This means it is 4 bytes on most JVMs and a redundant copy isn't going to make a big difference.

I argued that the clever compiler will see that the object is assigned to itself and will optimize it out.

Possibly, though I don't see it will make much difference either way.

While a compiler can remove redundant code, it is harder for a human to remove redundant code. In general we code with a purpose. When code has no purpose it is much harder to come to that conclusion. I suggest you avoid confusing anyone who has to read/maintain the code in the future.

As ever, when discussing performance, you should first consider clarity. How surprising in this construct and how easy is it to maintain.

The cost of a redundant assignment (which may or may not be optimised away) is nothing compared with the cost of even an easy to fix bug (never mind a hard to fix one)

You should focus on what is clearer (which is subjective) and not worry about micro-tuning the code unless you have a profiler which indicates this line is a performance issue.

For me, this is clearer, but even if it were slower, I would still argue it is clearer and easier to understand what it is doing.

if (myBigObject == null)
    myBigObject = createBigObject();

The fact you have already had to have a discussion but what that code is really doing, means you have spent more time than you can possibly recover.

In short, developer efficiency is usally more important than computer efficiency.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
2

Your colleague is wrong. After the first iteration myBigObject is no longer null and hence won't be created. Sample code proves this...

public static void main(String[] args) {
    Object myBigObject=null;

    int i=0;
    do {
        System.out.println("Iteration " + i);
        myBigObject=
                myBigObject==null?
                        createBigObject():
                        myBigObject;
       } while(i++ < 10);
    }

    private static Object createBigObject() {
        System.out.println("Creating bigObject");
        return new Object();
    }

The output of which is

Iteration 0
Creating bigObject
Iteration 1
Iteration 2
Iteration 3
Iteration 4
Iteration 5
Iteration 6
Iteration 7
Iteration 8
Iteration 9
Iteration 10

See how the create statement is only printed once

tddmonkey
  • 20,798
  • 10
  • 58
  • 67
  • Great minds think alike :-) Beat me to it, I'll remove my answer. – Mureinik Mar 03 '16 at 10:23
  • 1
    Please don't remove good answers. It's nice to have a selection. – Bathsheba Mar 03 '16 at 10:26
  • Thats right he doesn't object that the object construction is only called once: He say that when then assignment myBigObject=myBigobject ist done the compiler will create space in the memory to copy the Object -> copy it and then move the pointer to the new space and leave the original space to the garbage collection – Joachim Weiß Mar 03 '16 at 10:28
  • @JoachimWeiß no, in terms of pointers `myBigObject=myBigobject` just assigns the address value. You can check that by calling `System.identityHashCode(myBigObject)` - it should always return the same value after the object has been created, meaning it's the exact same instance and not a copy. – Thomas Mar 03 '16 at 10:32
1

Why does your colleague think that?

Only exactly one branch of a ternary conditional is ever evaluated. Imagine if it were not the case? So much code of the form obj == null ? null : obj.someproperty() would break!

So your object will only be created once.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483