I have trouble tracing and understanding how Java (and Javascript) is handling the following code:
// Java
int i = 0;
int[] a = {10, 20, 30, 40, 50, 60};
a[++i] = a[i++] = a[++i] = ++i;
System.out.println(Arrays.toString(a));
// [10, 4, 30, 4, 50, 60]
let i = 0;
const a = [10, 20, 30, 40, 50, 60];
a[++i] = a[i++] = a[++i] = ++i;
console.log( JSON.stringify(a)); // [10, 4, 30, 4, 50, 60]
When run, the output is [10, 4, 30, 4, 50, 60]
.
We can even simplify this example:
int i = 0;
int[] a = {10, 20};
a[i] = ++i;
System.out.println(Arrays.toString(a));
// [1, 20]
let i = 0;
const a = [10, 20];
a[i] = ++i;
console.log( JSON.stringify(a) ); // [1, 20]
I expected this to output [10, 1]
because right-hand-side first. But the output is [1, 20]
, but how?
3
Answers
No, in both languages the left hand side of the
=
is evaluated first, and then the right is.See 15.26.1. Simple Assignment Operator = from the JLS on the evaluation order of
=
when the LHS is an array access expression in Java (my emphasis):The ECMAScript spec (what JavaScript follows) also has runtime semantics for evaluating the left-hand side first in section 13.15.2:
So in both snippets (in both languages),
a[++i]
anda[i]
is evaluated first, givinga[1]
fora[++i]
anda[0]
fora[i]
as the indexes to be updated.I’m less familiar with Java, but for the Javascript: the current primitive value of
i
is being inserted whenever it’s encountered. There is no peek ahead to attempt to find a final value for it (if that’s what you meant by right-hand-first). So for the first example:The only odd thing going on here is that your second use of the increment operator is postfixed, while the others are all prefixed.
This mean the first two uses if
i
are both equal to1
– the second increment happens after the second value has been inserted. So it essentially reads:If you were to switch the second use to be
++i
like the others, you’d geta[1] = a[2] = a[3] = 4 // [10, 4, 4, 4, 50, 60]
(which possibly looks more like what you’d expect from the code at first glance).And the second example (in JS):
This is essentially reading:
To get your expected result of
[10,1]
, we’d need to move the incrementer to be before the first use ofi
:To understand why you get this result, let’s break down the operation:
in 1°) the value of i is incremented before being used, i.e. it is “1” and we are assigning a value to the second item of the array.
we change by 1°:
in 2°) i is incremented after being used, so it ends up being worth “2”, but the operation is performed with “1”, so item2° = item2°.
we change by 2°:
in 3°) we increase i again before using it, so it remains “3”.
we change by 3°:
in 4°) we increase again the value of i before using it, so it remains “4”.
we change by 4°:
reconstructing:
the multiple assignments, are made from right to left so:
this is why the second and fourth items are worth “4”.