11

In Java ArrayList<E> implementation base on a array of objects.
Can anybody explain me why implementation of ArrayList<E> uses array Object[] for data storage instead of E[]? What the benefit of using Object[]?

user2864740
  • 54,112
  • 10
  • 112
  • 187
user485553
  • 151
  • 2
  • 14

3 Answers3

8

In Java, creating an array of a generic type is not straightforward.

The simple approach does not compile:

public class Container<E> {

    E[] arr = new E[3]; // ERROR: Cannot create a generic array of E

}

Replace E with Object, and all is well (at the expense of added complexity elsewhere in the container implementation).

There are alternative approaches, but they present a different set of tradeoffs. For an extensive discussion, see How to create a generic array in Java?

Community
  • 1
  • 1
NPE
  • 438,426
  • 93
  • 887
  • 970
  • 3
    One could still do an unchecked cast `E[] arr = (E[]) new Object[3];` – Saintali Dec 08 '12 at 10:33
  • 1
    @Saintali: You could, but you will get a `ClassCastException` the moment you'll try to work with this array. Try it. – NPE Dec 08 '12 at 10:38
  • 3
    You wont in this case, because the array never escapes the `ArrayList` class. Try it :) – Saintali Dec 08 '12 at 10:43
  • 1
    @Saintali: I have, and I got the exception when I tried working with the array's elements. In any event, I think this debate is academic. – NPE Dec 08 '12 at 10:44
  • 1
    Technically, [it is possible](http://stackoverflow.com/questions/529085/java-how-to-generic-array-creation). But it would probably create some problems... – assylias Dec 08 '12 at 10:49
8

So first of all, realize that the actual runtime type of the array object must be Object[]. This is because arrays know their component types at runtime (different array types are actually different types at runtime), and thus you need to specify the component type in creating the array, but the ArrayList object does not know its type argument at runtime.

That said, the compile-time type of the instance variable could be declared as either Object[] or E[], with different advantages and disadvantages:

If it is declared as Object[]:

private Object[] arr;
// to create it:
arr = new Object[3];
// to get an element:
E get(int i) { return (E)arr[i]; }

The disadvantage of this is that you must cast it to E every time you take something out of it, which means you are basically using it as a pre-generics container.

If it is declared as E[]:

private E[] arr;
// to create it:
arr = (E[])new Object[3];
// to get an element:
E get(int i) { return arr[i]; }

The advantage of this is that you no longer have to cast when you get things out of it -- it provides type-checking on the uses of arr, like generic containers. The disadvantage is that, logically, the cast is lie -- we know we created an object whose runtime type is Object[], and so it is not an instance of E[], unless E is Object.

However, there is no immediate problem with doing this, because E is erased to Object inside the instance methods of the class. The only way a problem can occur is if the object is somehow exposed to the outside of the class (e.g. returned in a method, put in a public field, etc.) in a capacity that uses its type as E[] (which it's not):

// This would be bad. It would cause a class cast exception at the call site
E[] getArray() { return arr; }

But ArrayList, and indeed any properly-designed container class, would never expose an implementation detail such as its internal array to the outside. It would break abstraction, among other things. So as long as the author of this class is aware of not ever exposing this array, there is no problem with doing it this way (save perhaps confusing the next person who sees the code and is unaware of it), and is free to take advantage of the increased type-checking that this way brings.

newacct
  • 110,405
  • 27
  • 152
  • 217
  • Upvoted for the detailed analysis of the tradeoff. But IMHO although the second approach works, it should be discouraged. `E[]` (or really any array type like `String[]` and `Integer[]`, except `Object[]`), is fundamentally different from `Object[]`. Casting this way gives the illusion that they have a subtype relationship, which is not the case. On the other hand, casting `Object` to `E` makes much more sense, not only because `E` is always a subclass of `Object` so it's conceptually feasible, but at runtime the element in the array must be of type `E`. – wlnirvana Feb 06 '21 at 11:20
4

Considering type erasure (that is, the fact that generics type parameters such as E in your example are deleted at compilation type), I suspect the generated bytecode would be similar in both cases.

From a maintenance point of view, using a type parameter instead of Object would lead to easier to read code (since it would limit casts). But since the API of ArrayList never exposes that "raw" Object array, I suppose it does not make any difference for us mere Java developers :)

oparisy
  • 1,717
  • 2
  • 14
  • 16