Joh*_*ler 17 java arrays generics arraylist
ArrayList在内部使用Object数组:
private transient Object[] elementData;
Run Code Online (Sandbox Code Playgroud)
并且在E get(int)
方法中它被转换为E类型.
我的问题是:为什么ArrayList不使用E []来存储对象?
我知道在编译器运行之后,类型擦除会将E []转换为Object [],但是仍然需要在每个get()调用中转换为E?
如果使用E [],则无需使用以下代码
return (E) elementData[index];
Run Code Online (Sandbox Code Playgroud)
使用Object []的选择是为了表现?
当类型擦除将E []转换为Object []时,java会在内部进行强制转换以在泛型方法中返回正确的类型吗?
EDITED
让我更好地解释一下我的疑点是什么:
如果ArrayList使用E []而不是Object [],则在方法get(int)中不需要强制转换.这将提高性能(显然).
但是,没有魔法,我认为使用E [] JVM无论如何都会抛出对象,因为类型擦除会在Object中转换.正确?
ps:抱歉我的英语不好.
Tav*_*nes 13
更新:这个答案得到了更多的关注和支持比我认为基本上复制粘贴JDK源代码所应得的,因此我将尝试将其变成值得的东西.
Java泛型的外观和感觉都是真实的,具体化的,多实例化的,C++或C#样式的泛型.这意味着对于类似的类型ArrayList<E>
,我们希望ArrayList<String>
表现得像每次出现E
都被替换掉了String
.换句话说,这个:
private Object[] elementData = new Object[size];
public E get(int i) {
return (E) elementData[i];
}
String str = list.get(0);
Run Code Online (Sandbox Code Playgroud)
应该成为这样的:
private Object[] elementData = new Object[size];
public String get(int i) {
return (String) elementData[i];
}
String str = list.get(0);
Run Code Online (Sandbox Code Playgroud)
现在,正如您可能知道的那样,实际上并不是它们的工作方式.对于现在(大部分)远远落后于我们的向后兼容性原因,Java泛型通过类型擦除实现,其中E
实际上被替换为Object
无处不在,并且在必要时将强制转换String
插入到调用代码中.这意味着代码实际上变成了这样的:
private Object[] elementData = new Object[size];
public Object get(int i) {
return elementData[i];
}
String str = (String) list.get(0);
Run Code Online (Sandbox Code Playgroud)
演员(E)
已经消失,并在通话现场重新出现.如果呼叫站点忽略了结果,则演员阵容将完全消失!这就是为什么它给出了"未经检查"的警告.
现在想象一下,如果elementData
有类型E[]
,请按照你的建议.也就是说,代码看起来像这样:
private E[] elementData = (E[]) new Object[size];
public E get(int i) {
return elementData[i];
}
String str = list.get(0);
Run Code Online (Sandbox Code Playgroud)
我们知道由于擦除,它会变成与上面相同的东西.但是,如果我们按照我们希望的方式修改了泛型,它将如下所示:
private String[] elementData = (String[]) new Object[size];
// ClassCastException: Object[] is not a String[]
Run Code Online (Sandbox Code Playgroud)
基本上我们编写了一些应该在运行时崩溃的代码,它的唯一原因就是Java的泛型实现假装比它更好.我们欺骗编译器说服它接受脆弱的代码.
它很脆弱!我们碰巧避免运行时崩溃,因为数组永远不会逃避类.但如果确实如此,那将导致ClassCastException
难以预测的地方.如果Java 9引入了具体的泛型怎么办?第一个实现将继续工作,但这个会破坏.
这就是为什么大多数合理的Java编码约定要求未经检查的强制类型转换为类型正确的原因. (E) elementData[i]
是类型正确的,因为ArrayList
确保只能E
存储s elementData
. (E[]) new Object[size]
从来都不是类型正确的,除非E
是Object
.
还有其他好处.在Java 8中,该elementData
字段可以采用特殊的sentinel值:
/**
* Shared empty array instance used for empty instances.
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*/
transient Object[] elementData; // non-private to simplify nested class access
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
1199 次 |
最近记录: |