skip to Main Content

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


  1. I expected this to output [10, 1] because right-hand-side first

    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):

    If the left-hand operand is an array access expression (§15.10.3),
    possibly enclosed in one or more pairs of parentheses, then:

    • First, the array reference subexpression of the left-hand operand
      array access expression is evaluated…

    • Otherwise, the index subexpression of the left-hand operand array
      access expression is evaluated. If this evaluation completes abruptly,
      then the assignment expression completes abruptly for the same reason
      and the right-hand operand is not evaluated and no assignment occurs.

    • Otherwise, the right-hand operand is evaluated.

    The ECMAScript spec (what JavaScript follows) also has runtime semantics for evaluating the left-hand side first in section 13.15.2:

    1. If LeftHandSideExpression is neither an ObjectLiteral nor an
      ArrayLiteral, then

      a. Let lref be ? Evaluation of
      LeftHandSideExpression.


    So in both snippets (in both languages), a[++i] and a[i] is evaluated first, giving a[1] for a[++i] and a[0] for a[i] as the indexes to be updated.

    Login or Signup to reply.
  2. 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:

    let i = 0;
    const a = [10, 20, 30, 40, 50, 60];
    a[++i] = a[i++] = a[++i] = ++i;
    console.log(a);
    // [10, 4, 30, 4, 50, 60]
    

    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 to 1 – the second increment happens after the second value has been inserted. So it essentially reads:

    a[1] = a[1] = a[3] = 4;
    

    If you were to switch the second use to be ++i like the others, you’d get a[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):

    let i = 0;
    const a = [10,20];
    a[i] = ++i;
    console.log(a); // [1, 20]
    

    This is essentially reading:

    a[0] = 1;
    

    To get your expected result of [10,1], we’d need to move the incrementer to be before the first use of i:

    let i = 0;
    const a = [10,20];
    a[++i] = i;
    console.log(a);
    
    Login or Signup to reply.
  3. To understand why you get this result, let’s break down the operation:

        i = 0
        [ 10, 20, 30, 40, 50, 60 ]
    
        // 1°      // 2°      // 3°      // 4°
        a[ ++i ] = a[ i++ ] = a[ ++i ] = ++i;
    

    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°:

        arr[ 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°:

        arr[ 1 ]
    

    in 3°) we increase i again before using it, so it remains “3”.

    we change by 3°:

        arr[ 3 ]
    

    in 4°) we increase again the value of i before using it, so it remains “4”.

    we change by 4°:

        4
    

    reconstructing:

        arr[ 1 ] = arr[ 1 ] = arr[ 3 ] = 4
    

    the multiple assignments, are made from right to left so:

        arr[ 3 ] = 4
        arr[ 1 ] = arr[ 3 ] // 4
        arr[ 1 ] = arr[ 1 ]
    

    this is why the second and fourth items are worth “4”.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search