Roberto Roberto - 28 days ago 21
Java Question

Seeming Discrepancy in Arrays.copyOf

Why this question is not a possible duplication of How Arrays.asList(int[]) can return List<int[]>?.
That question doesn't really answer my particular situation as I am trying to figure out if there is a discrepancy in my use of Arrays.copyOf.

CASE 1: Supposed deep copy of the array

// Creating a integer array, populating its values
int[] src = new int[2];
src[0] = 2;
src[1] = 3;
// Create a copy of the array
int [] dst= Arrays.copyOf(src,src.length);
Assert.assertArrayEquals(src, dst);
// Now change one element in the original
dst[0] = 4;
// Following line throws an exception, (which is expected) if the copy is a deep one
Assert.assertArrayEquals(src, dst);


CASE 2:
Here is where things seem to be weird:
What I am trying to do with the below method (lifted verbatim from a book) is to create an immutable list view of a copy of the input array arguments. That way, if the input array changes, the contents of the returned list don't change.

@SafeVarargs
public static <T> List<T> list(T... t) {
return Collections.unmodifiableList(new ArrayList<>(Arrays.asList(Arrays.copyOf(t, t.length))));
}


int[] arr2 = new int[2];
arr2[0] = 2;
arr2[1] = 3;
// Create an unmodifiable list
List<int[]> list2 = list(arr2);

list2.stream().forEach(s -> System.out.println(Arrays.toString(s)));
// Prints [2, 3] as expected

arr2[0] = 3;

list2.stream().forEach(s -> System.out.println(Arrays.toString(s)));
// Prints [3, 3] which doesn't make sense to me... I would have thought it would print [2, 3] and not be affected by my changing the value of the element.


The contradiction that I see is that in one case (Case 1), Arrays.copyOf seems to be a deep copy, whereas in the other case (Case 2), it seems like a shallow one. The changes to the original array seem to have written through to the list, even though I have copied the array in creating my unmodifiable list.

Would someone be able to help me resolve this discrepancy?

Answer

First of all, your list method performs an unnecessary step, you don't need the copyOf operation, so here goes:

@SafeVarargs
public static <T> List<T> list(T... t) {
    return Collections.unmodifiableList(
        new ArrayList<>(Arrays.asList(t))
    );
}

The ArrayList constructor already copies the incoming list, so you're safe there.

Next, when you are calling your list() method with an int[], that array is considered to be a single element of type int[], because the type erasure of your T... is Object..., and int is primitive. There is no way you can make your method do a deep copy inside the list without either changing the parameter types or doing an instanceOf check and performing the copy manually inside the method. I'd say the wisest thing to do is probably to move the Arrays.copyOf() call outside the method:

List<int[]> list2 = list(Arrays.copyOf(arr2));
Comments