Java ArrayList<T>
(可能还有许多其他类)中发生的事情是内部Object[] array = new Object[n];
,T
个对象写入其中。每当从中读取元素时,都会执行转换return (T) array[i];
。所以,每次阅读时都会有演员。
我想知道为什么这样做了。对我来说,似乎他们只是做了不必要的演员表。在没有演员的情况下创建T[] array = (T[]) new Object[n];
然后只是return array[i];
会不会更合乎逻辑,也更快一点?这只是每个数组创建一个强制转换,通常远小于读取次数。
为什么他们的方法首选?我不明白为什么我的想法不是更严格?
答案 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 [])对象类型而不是数组中的元素。