2014-09-05 86 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

相關問題