简体   繁体   English

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

[英]Why does Java ArrayList use per-element casting instead of per-array casting?

What happens inside Java's ArrayList<T> (and probably many other classes) is that there is an internal Object[] array = new Object[n]; Java的ArrayList<T> (可能还有很多其他类)中发生的事情是内部Object[] array = new Object[n]; , to which T Objects are written. ,写入T对象。 Whenever an element is read from it, a cast return (T) array[i]; 每当从中读取一个元素时,一个强制转换return (T) array[i]; is done. 已经完成了。 So, a cast on every single read. 所以,每次阅读时都要施展。

I wonder why this is done. 我想知道为什么这样做。 To me, it seems like they're just doing unnecessary casts. 对我来说,似乎他们只是做了不必要的演员表。 Wouldn't it be more logical and also slightly faster to just create a T[] array = (T[]) new Object[n]; 创建一个T[] array = (T[]) new Object[n];是不是更合乎逻辑,也更快一点T[] array = (T[]) new Object[n]; and then just return array[i]; 然后只return array[i]; without cast? 没有演员? This is only one cast per array creation, which is usually far less than the number of reads. 这只是每个数组创建一个强制转换,通常远远小于读取次数。

Why is their method to be preferred? 为什么他们的方法是首选的? I fail to see why my idea isn't strictly better? 我不明白为什么我的想法不是更严格?

It's more complicated than that: generics are erased in byte code, and the erasure of T[] is Object[] . 它比这更复杂:泛型以字节代码擦除, T[]的擦除是Object[] Likewise, the return value of get() becomes Object . 同样, get()的返回值变为Object To retain integrity of the type system, a checked cast is inserted when the class is actually used, ie 为了保持类型系统的完整性,在实际使用类时插入检查的强制转换,即

Integer i = list.get(0);

will be erased to 将被删除

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

That being the case, any type check within ArrayList is redundant. 既然如此,ArrayList中的任何类型检查都是多余的。 But it's really beside the point, because both (T) and (T[]) are unchecked casts, and incur no runtime overhead. 但它确实不重要,因为(T)(T[])都是未经检查的强制转换,并且不会产生运行时开销。

One could write a checked ArrayList that does: 可以编写一个检查过的ArrayList:

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

This would prevent heap pollution , but at the price of a redundant type check (you can not suppress the synthentic cast in calling code). 这样可以防止堆污染 ,但代价是冗余类型检查(你无法抑制调用代码中的合成转换)。 It would also require the caller to provide the ArrayList with the class object of the element type, which clutters its api and makes it harder to use in generic code. 它还需要调用者为ArrayList提供元素类型的类对象,这会使其api变得混乱并使其在通用代码中更难使用。

Edit: Why is generic array creation forbidden? 编辑:为什么禁止创建通用数组?

One problem is that arrays are checked, while generics are unchecked. 一个问题是检查数组,而未检查泛型。 That is: 那是:

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

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

Therefore, the component type of an array matters. 因此,阵列的组件类型很重要。 I assume this is why the designers of the Java language require us to be explicit about which component type to use. 我假设这就是Java语言的设计者要求我们明确使用哪种组件类型的原因。

Whenever an element is read from it, a cast return (T) array[i]; 每当从中读取一个元素时,一个强制转换return (T) array[i]; is done. 已经完成了。 So, a cast on every single read. 所以,每次阅读时都要施展。

Generic is a compile time check. Generic是编译时检查。 At runtime the type T extends is used instead. 在运行时,使用类型T extends代替。 In this case T implicitly extends Object so what you have at runtime is effectively. 在这种情况下, T隐式extends Object因此您在运行时所拥有的是有效的。

return (Object) array[i];

or 要么

return array[i];

Wouldn't it be more logical and also slightly faster to just create a 创建一个不是更合乎逻辑,也更快一点

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

Not really. 并不是的。 Again at runtime this becomes 在运行时再次成为

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

or 要么

Object[] array = new Object[n];

What you are really fishing for is 你真正钓鱼的是

T[] array = new T[n];

except this doesn't compile, mostly because T isn't known at runtime. 除了这不编译,主要是因为T在运行时不知道。

What you can do is 你能做的是

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

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

only then will the array actually be the type expected. 只有这样,数组实际上才是预期的类型。 This could make reads faster, but at the cost of writes. 这可以使读取速度更快,但代价是写入。 The main benefit would be a fail fast check, ie you would stop a collection being corrupted rather than wait until you detect it was corrupted to throw an Exception. 主要的好处是快速检查失败,即你会阻止一个集合被破坏,而不是等到你发现它被破坏后抛出异常。

I think is more a matter of code style not of performance or type safety (because the backing array is private) 我认为更多的是代码风格而不是性能或类型安全(因为支持数组是私有的)

The java 5 ArrayList was implemented the way you suggested with an E[] array. java 5 ArrayList实现方式与您对E[]数组的建议方式相同。 If you look at the source code you see that it contains 7 (E[]) casts. 如果您查看源代码,您会看到它包含7个(E [])强制转换。 From java 6 the ArrayList changed to use an Object[] array which resulted in only 3 (E) casts. 从java 6开始, ArrayList更改为使用Object[]数组,该数组仅导致3(E)个转换。

Array is object too. 数组也是对象。 Here T[] array = (T[]) new Object[n] you cast only (T[]) object type not elements in the array. 这里T[] array = (T[]) new Object[n]你只投射(T [])对象类型而不是数组中的元素。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM