2014-09-05 38 views
10

ArrayList的使用对象阵列内部:为什么ArrayList在内部使用Object [](而不是E [])?

private transient Object[] elementData; 

E get(int)方法它浇铸到E型。

我的问题是:为什么ArrayList不使用E []来存储对象?我知道编译器运行后,类型擦除会将E []转换为Object [],但是仍然需要在每次get()调用时转换为E?

如果用它E []下面这段代码是没有必要的

return (E) elementData[index]; 

使用Object []对象的选择是性能?

由于type-erasure将E []转换为Object [],java会在内部进行强制类型转换以在泛型方法中返回正确类型?

EDITED

让我更好地说明什么是我的疑问点:

如果ArrayList中使用E []而不是[对象],在方法得到(INT)投不必要。 这会提高性能(显然)。

但是,没有魔法,我认为使用E [] JVM将投射对象,因为type-erasure将在Object中转换。正确?

ps:抱歉我的英语不好。

+0

您似乎认为“E”是真实的。这是你想象中的无花果牛顿。在ArrayList类被编译时,数组的类是固定的,回到Java代工中。 – 2014-09-05 22:59:23

+0

@HotLicks“据我所知,编译器运行后,类型擦除将E []转换为Object []”..“如果使用它E [],下面的代码[它不是]必须[使用强制转换]” – user2864740 2014-09-05 23:00:04

+0

声明E []可能会改进源代码,但最终的编译结果将是相同的。 – 2014-09-05 23:00:28

回答

8

更新:这个回答得到了更多的关注和upvotes比我认为它应得的基本上复制粘贴的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); 

应该成为这样的:

private Object[] elementData = new Object[size]; 

public String get(int i) { 
    return (String) elementData[i]; 
} 

String str = list.get(0); 

现在,你可能知道,那并不是他们如何工作。对于现在(大部分)很长时间后面的兼容性原因,Java泛型是通过类型擦除实现的,其中E实际上被替换为Object,并且铸造到String被插入到中,并在需要时调用代码。这意味着该代码实际上变成是这样的:

private Object[] elementData = new Object[size]; 

public Object get(int i) { 
    return elementData[i]; 
} 

String str = (String) list.get(0); 

演员到(E)已经消失,并在调用点又出现了。如果通话网站忽略了结果,剧组将完全消失!这就是为什么它给出了“未经检查”的警告。


现在想象一下,如果elementData有型E[]相反,你的建议。也就是说,代码如下所示:

private E[] elementData = (E[]) new Object[size]; 

public E get(int i) { 
    return elementData[i]; 
} 

String str = list.get(0); 

由于擦除,我们知道它被转换为与上述相同的东西。但是如果我们有具体化的泛型像我们希望我们所做的,它应该是这样的:

private String[] elementData = (String[]) new Object[size]; 
// ClassCastException: Object[] is not a String[] 

本质上讲,我们已经写了一些代码,将会在运行时崩溃,它在所有工作的唯一原因是,Java的泛型实施假装比现在更好。我们向编译器撒谎以说服它接受脆弱的代码。

它很脆!我们碰巧避免了运行时崩溃,因为数组永远不会转义类。但是如果这样做的话,它会导致难以预测的地方出现ClassCastException。如果Java 9引入了泛化的泛型呢?第一个实现会继续工作,但是这个会破坏。

这就是为什么大多数合理的Java编码规范要求非检查类型转换为类型正确的原因。 (E) elementData[i]是类型正确的,因为ArrayList确保只有E s可以存储在elementData中。 (E[]) new Object[size]从来没有类型正确,除非EObject


还有其他的好处。在Java 8中,elementData字段可以具有特殊的哨兵值:

/** 
* 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 
+0

我不确定这些特殊哨兵值的存在是*的原因,还是*将* elementData; *声明为'Object []'的结果。 – Pshemo 2014-09-05 23:17:38

+2

@Pshemo'static E []'是非法的 – 2014-09-05 23:18:52

+2

他们仍然可以将'static'字段保留为'Object []'并且具有'E []'类型的实例字段,并在赋值时进行转换。 – 2014-09-05 23:21:17

相关问题