How to copy an array

A bug has been found… (No bug has been found (edit))
Explanation follows

void setup() {
  double[] a = {1, 2, 3};
  double[] b = {1, 2, 3};
  for (double print : b) {
    print(print + ", ");
  }
  println();
  b = new Testos(a).running();
  for (double print : b) {
    print(print + ", ");
  }
  println();
}

public class Testos {
  double[] a;
  double[] b;
  Testos(double[] a) {
    this.a = a;
    this.b = a;
  }
  public double[] running() {
    for (int i = 0; i < this.a.length; i++) {
      this.a[i] = this.b[i]+5;
    }
    return this.b;
  }
}

Output:

1.0, 2.0, 3.0,
6.0, 7.0, 8.0,

Expected Output:

1.0, 2.0, 3.0,
1.0, 2.0, 3.0,

I think the expected output has to be correct, because the class Testos has 2 vars a and b. b is assigned to a at the very beginning. There a is 1.0, 2.0, 3.0. Then b is not touched any more in any way. It should still be 1.0, 2.0, 3.0. However, it isn’t. The returned b has been changed. It is now equal to a, a being 6.0, 7.0, 8.0.

My estimation is that the double passed to the constructor dereferences b and references it to a. Consequently, invoking changes made to a also to b, eventhough these changes are not desired.

Testos(double[] a) {
  this.a = a;
  this.b = a;
}

Looking for a workaround.

Greetings, Lucas

Nope, it‘s right. It‘s because arrays are only referenced by assigning them with a = b.
Example :
double[] a = {1.0, 2.0, 3.0};
double[] b = a;
b[0] = 100.0;
//a = {100.0,2.0,3.0}
//b = {100.0, 2.0, 3.0}

You can use b = arrayCopy(a); to avoid this.

1 Like

To copy an array, you need to create a new array and copy the elements manually or with arrayCopy() or use java.util.Arrays.copyOf. Otherwise both a and b refer to the same instance, as you found out yourself. This also applies to all objects (exception are primitives like int, boolean, or float, which are copied).

2 Likes

Just directly invoke method clone() over the array container: :nerd_face:

class Testos {
  final float[] a, b;

  Testos(final float... arr) {
    b = (a = arr).clone();
  }
}

@Lexyth, @Jakub : I haven’t explored the java methods yet, but arrayCopy() doesn’t seem to work on 2-d arrays. Does anyone know why this is?

void setup(){
  int[][] array1 = new int[2][5];
  //  initialize array1  
  for(int j = 0; j < 5; j++){
    for(int i = 0; i < 2; i++){
      array1[i][j] = int(random(2*5));
    }
  }
  
  int[][] array2 = new int[2][5];  
  //  initialize array2  
  for(int j = 0; j < 5; j++){
    for(int i = 0; i < 2; i++){
      array2[i][j] = int(random(2*5));
    }
  }

  println("array1 and array2 both initialized:");
  //  print both arrays
  print("array1: ");
  for(int j = 0; j < 5; j++){
    for(int i = 0; i < 2; i++){
      print(array1[i][j] + " ");
    }
  }  println();
  print("array2: ");
  for(int j = 0; j < 5; j++){
    for(int i = 0; i < 2; i++){
      print(array2[i][j] + " ");
    }
  }  println();
  println();

  //  arrayCopy() links array1 and array2!
  println("copy array1 to array2:");
  arrayCopy(array1, array2);

  //  print both arrays
  print("array1: ");
  for(int j = 0; j < 5; j++){
    for(int i = 0; i < 2; i++){
      print(array1[i][j] + " ");
    }
  }  println();
  print("array2: ");
  for(int j = 0; j < 5; j++){
    for(int i = 0; i < 2; i++){
      print(array2[i][j] + " ");
    }
  }  println();
  println();
  

  println("new values generated for array1 only:");
  for(int j = 0; j < 5; j++){
    for(int i = 0; i < 2; i++){
      array1[i][j] = int(random(2*5));
    }
  }
  
  //  print both arrays
  print("array1: ");
  for(int j = 0; j < 5; j++){
    for(int i = 0; i < 2; i++){
      print(array1[i][j] + " ");
    }
  }  println();
  print("array2: ");
  for(int j = 0; j < 5; j++){
    for(int i = 0; i < 2; i++){
      print(array2[i][j] + " ");
    }
  }  println();
  println();
  
  println("new values generated for array2 only:");
  for(int j = 0; j < 5; j++){
    for(int i = 0; i < 2; i++){
      array2[i][j] = int(random(2*5));
    }
  }
  
  //  print both arrays
  print("array1: ");
  for(int j = 0; j < 5; j++){
    for(int i = 0; i < 2; i++){
      print(array1[i][j] + " ");
    }
  }  println();
  print("array2: ");
  for(int j = 0; j < 5; j++){
    for(int i = 0; i < 2; i++){
      print(array2[i][j] + " ");
    }
  }  println();
  println();

}


void draw(){}

The reference for arrayCopy() states that it only copies references to each object in the base array (base array meaning the first brackets in any multidimensional array). If you have a 2D array, you have a base array storing other arrays. All arrayCopy() does is copy the references to the other arrays into a new array. In your case, the references are referencing other 1D arrays storing ints. So, your new array is technically 2D, but any changes made to one reflect in the other because they are modifying the same references. If you want to deep copy arrays (create new references instead of reusing old ones), you will likely need to use a for loop to assign new values.

If you want to learn more about copying data in Java, read further.

If you are using primitives, like int or float, you can create new references by writing array2[i][j] = array1[i][j]; because assigning primitives with = does not reuse references.
If you are using Objects, like String or something else, using = does reuse a reference. To overcome this, you need to create a new Object using the new operator. Because the new operator expects a constructor afterward, you likely need a copy constructor which deep copies all instance variables in the object. The String copy constructor looks like String copy = new String(original); and changes to copy will not affect the original. For a String array, you could use array2[i][j] = new String(array1[i][j]);.

2 Likes

Thanks @Shwaa for the clarification and examples, especially the String copy constructor. That gives me something to work with. Cheers!

Hi @GoToLoop,

What is the meaning of the three stops ... in your above example? I don’t see anything in the Processing reference or the Java API.

Also, why use final? It seems that both arrays a and b are mutable.

Thanks in advance,
sb9k

That’s used for a variable argument parameter.
It means we can pass an array or any number of arguments to that parameter.
Such parameter behaves as an array inside its function.

Keyword final prohibits a variable to be re-assigned once it’s initialized.
Keyword final doesn’t turn the object a variable points to immutable.

Thanks for the prompt reply, GoToLoop. You can see, perhaps, my confusion having read the following on the Processing reference page final:

Keyword used to state that a value, class, or method can’t be changed. If the final keyword is used to define a variable, the variable can’t be changed within the program.

The example code given there won’t even compile. Should the page be updated/expanded/clarified?
[ time passes ]
I see that the behaviour is supposed to different for arrays than for primitives.

Remaining questions:

  • Why did you choose to use final is this example?
  • What are the benefits?
  • What are the drawbacks, if any?
  • And, please, why use the intermediary assignment of (a = arr)?

Peace,
sb9k

I’m used to apply final to any variable I don’t want to be re-assigned.

We can’t re-assign final variables by accident.

Also b/c a final variable got its value permanently sealed we can rest assured when its value is read that will be always the same.

Well, pretty much nothing really.

Of course we’ll have to remove the final keyword when we change our mind and need the variable to be re-assignable again.

On C-based programming languages like Java & JS even the assignment = operator can be used within an expression.

I’ve just felt like to use that feature there.

I couldn’t do that in Python btW.

The way expression b = (a = arr).clone(); works is 1st field a receives the value of parameter arr.

Right after that method clone() is invoked on that same reference value creating a shallow copy of the array which field a now references to.

Finally field b receives the reference value of that cloned array.