1

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?

Community
  • 1
  • 1
Roberto
  • 31
  • 4
  • 4
    There are so many issues here. You're passing your `int[]` to a varargs method. It gets wrapped into a `Object[]` which you pass to `copyOf`. `copyOf` therefore makes a copy of the `Object[]` containing a single `int[]`. That `Object[]` with a single `int[]` then gets wrapped into a `List` with `asList`, containing just the `int[]`. Then the elements in that list (the `int[]` gets copied into a new `ArrayList`. It's the same `int[]` object all the way down. – Sotirios Delimanolis Nov 07 '16 at 15:35
  • Thanks, Sotirios. If I understand you correctly what is being copied is the Object[] (which holds the int[]) and therefore I'm not actually making a copy of the int[] that I think I am passing into the list() method. In the first case, it's a direct operation on an int[], so what I see are changes to the copy of the array. – Roberto Nov 07 '16 at 15:54

1 Answers1

0

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));
Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588
  • Thanks Sean. That makes a lot of sense. I tried your alternate suggestions as well to satisfy my own curiosity, and those worked. Thanks again. – Roberto Nov 07 '16 at 16:33