为什么Java ArrayList使用每个元素的转换而不是每个数组的转换?

时间:2012-09-11 08:50:52

标签: java casting arraylist

Java ArrayList<T>(可能还有许多其他类)中发生的事情是内部Object[] array = new Object[n];T个对象写入其中。每当从中读取元素时,都会执行转换return (T) array[i];。所以,每次阅读时都会有演员。

我想知道为什么这样做了。对我来说,似乎他们只是做了不必要的演员表。在没有演员的情况下创建T[] array = (T[]) new Object[n];然后只是return array[i];会不会更合乎逻辑,也更快一点?这只是每个数组创建一个强制转换,通常小于读取次数。

为什么他们的方法首选?我不明白为什么我的想法不是更严格?

4 个答案:

答案 0 :(得分:18)

比这更复杂:泛型以字节代码擦除,T[]的擦除为Object[]。同样,get()的返回值变为Object。为了保持类型系统的完整性,在实际使用类时插入一个经过检查的强制转换,即

Integer i = list.get(0);

将被删除

Integer i = (Integer) list.get(0);

在这种情况下,ArrayList中的任何类型检查都是多余的。但它确实不重要,因为(T)(T[])都是未经检查的强制转换,并且不会产生运行时开销。

可以编写一个检查过的ArrayList:

T[] array = Array.newInstance(tClass, n);

这可以防止堆污染,但是以冗余类型检查的代价(你无法抑制调用代码中的合成强制转换)。它还需要调用者为ArrayList提供元素类型的类对象,这会使其api变得混乱并使其更难用于通用代码。

编辑:为什么禁止创建通用数组?

一个问题是检查数组,而未检查泛型。那就是:

Object[] array = new String[1];
array[0] = 1; // throws ArrayStoreException

ArrayList list = new ArrayList<String>();
list.add(1); // causes heap pollution

因此,数组的组件类型很重要。我假设这就是Java语言的设计者要求我们明确使用哪种组件类型的原因。

答案 1 :(得分:4)

  

每当从中读取元素时,都会执行强制转换return (T) array[i];。所以,每次阅读时都会有演员。

Generic是一个编译时间检查。在运行时,使用类型T extends代替。在这种情况下T隐式extends Object所以你在运行时有效。

return (Object) array[i];

return array[i];
  

创建

不是更合乎逻辑,也更快一点
T[] array = (T[]) new Object[n]

不是真的。再次在运行时,这变为

Object[] array = (Object[]) new Object[n];

Object[] array = new Object[n];

你真正钓鱼的是

T[] array = new T[n];

除了这不能编译,主要是因为在运行时不知道T。

你能做的是

private final Class<T> tClass; // must be passed in the constructor

T[] array = (T[]) Array.newInstance(tClass, n);

只有这样,数组实际上才是预期的类型。这可以使读取速度更快,但代价是写入。主要的好处是快速检查失败,即你会阻止一个集合被破坏,而不是等到你发现它被破坏后抛出异常。

答案 2 :(得分:2)

我认为更多的是代码风格而不是性能或类型安全(因为支持数组是私有的)

java 5 ArrayList的实现方式与E[]数组的建议方式相同。如果您查看源代码,您会看到它包含7个(E [])强制转换。从java 6开始,ArrayList更改为使用Object[]数组,结果只有3(E)个转换。

答案 3 :(得分:-1)

数组也是对象。 这里T[] array = (T[]) new Object[n]只投射(T [])对象类型而不是数组中的元素。