为什么ArrayList实现使用Object []?

时间:2012-12-08 10:22:30

标签: java generics arraylist

在Java ArrayList<E>实现基础上的一个对象数组。
任何人都可以解释为什么ArrayList<E>的实现使用数组Object[]代替E[]进行数据存储?使用Object[]的好处是什么?

3 个答案:

答案 0 :(得分:8)

在Java中,创建泛型类型的数组并不简单。

简单方法无法编译:

public class Container<E> {

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

}

E替换为Object,一切都很好(代价是在容器实现的其他地方增加了复杂性)。

有其他方法,但它们提供了一组不同的权衡。有关详细讨论,请参阅How to create a generic array in Java?

答案 1 :(得分:5)

首先,要意识到数组对象的实际运行时类型必须是Object[]。这是因为数组在运行时知道它们的组件类型(不同的数组类型在运行时实际上是不同的类型),因此您需要在创建数组时指定组件类型,但ArrayList对象不知道它的类型参数在运行时。

也就是说,实例变量的编译时类型可以声明为Object[]E[],具有不同的优点和缺点:

如果声明为Object[]

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

这样做的缺点是每次从中取出时都必须将其强制转换为E,这意味着您基本上将它用作预先通用的容器。

如果声明为E[]

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

这样做的好处是,当您从中获取内容时,您不再需要进行强制转换 - 它提供了arr的使用类型检查,就像通用容器一样。缺点是,从逻辑上讲,演员阵容是谎言 - 我们知道我们创建了一个运行时类型为Object[]的对象,因此它不是E[]的实例,除非EObject

但是,执行此操作不会立即出现问题,因为E在类的实例方法中被删除为Object。问题出现的唯一方法是,如果对象以某种方式暴露给类的外部(例如,在方法中返回,放入公共字段等),其容量使用其类型为E[](它不是):

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

但是ArrayList,实际上任何设计合理的容器类,都不会将实现细节(如内部数组)暴露给外部。除其他外,它会破坏抽象。因此,只要这个类的作者知道没有暴露这个数组,这样做是没有问题的(保存可能会让下一个看到代码并且不知道它的人感到困惑),并且可以自由地使用它这种方式增加了类型检查的优势。

答案 2 :(得分:2)

考虑type erasure(即,在编译类型中删除了示例中的E等泛型类型参数这一事实),我怀疑生成的字节码在两种情况下都是相似的。

从维护的角度来看,使用类型参数而不是Object会导致更容易阅读代码(因为它会限制强制转换)。但由于ArrayList的API从未公开过那个“原始”Object数组,我认为它对我们仅仅是Java开发人员没有任何影响:)