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

Java的ArrayList (可能还有很多其他类)中发生的事情是内部Object[] array = new Object[n]; ,写入T对象。 每当从中读取一个元素时,一个强制转换return (T) array[i]; 已经完成了。 所以,每次阅读时都要施展。

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

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

它比这更复杂:generics用字节代码擦除, 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变得混乱,并使得在通用代码中使用起来更加困难。

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

一个问题是检查数组,而不检查generics。 那是:

 Object[] array = new String[1]; array[0] = 1; // throws ArrayStoreException ArrayList list = new ArrayList(); list.add(1); // causes heap pollution 

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

每当从中读取一个元素时,一个强制转换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 tClass; // must be passed in the constructor T[] array = (T[]) Array.newInstance(tClass, n); 

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

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

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

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