2010-04-08 57 views
11

Autoboxing是相當可怕的。雖然我完全理解==.equals之間的區別我不能不幫助有後續錯誤的地獄了我:爲什麼編譯器/ JVM不能讓自動裝箱「只是工作」?

final List<Integer> foo = Arrays.asList(1, 1000); 
    final List<Integer> bar = Arrays.asList(1, 1000); 
    System.out.println(foo.get(0) == bar.get(0)); 
    System.out.println(foo.get(1) == bar.get(1)); 

,打印

true 
false 

他們爲什麼這樣做這種方式?這與緩存的整數有關,但如果是這種情況,爲什麼不緩存該程序使用的所有整數?或者爲什麼JVM不總是自動取消原始的框?

打印錯誤的假或真的真的會更好。

編輯

我不同意有關的舊代碼破損。通過讓foo.get(0) == bar.get(0)返回true,您已經破壞了代碼。

不能在此可以在編譯器級別由字節碼與詮釋更換整數(只要它永遠不會賦值爲null)

+8

它只是工作!不是你期望的方式;) – OscarRyz 2010-04-08 19:14:33

+0

實際上你的例子與自動裝箱無關,它的行爲早於它。確實,自動裝箱迫使我們更加意識到它:equals()是比較對象的(通常是正確的)方式,==是比較原語的(唯一)方法... autoboxing幫助程序員處理Integer和int (幾乎)可以互換......因此存在bug的危險。 – leonbloy 2010-04-08 19:18:22

+0

這可能是由於也影響String實例的大小寫。就我所瞭解的情況而言,根據價值在幕後發生了一些聯營活動。這可以通過使用'new'關鍵字來防止。 – 2010-04-08 19:33:23

回答

9
  • 他們爲什麼這樣做這種方式?

-128和127之間的每個整數都被java高速緩存。據說,他們這樣做是爲了獲得性能優勢。即使他們現在想回到這個決定,他們也不可能。如果有人根據此代碼構建代碼,那麼代碼在被取出時會中斷。對於業餘愛好編碼而言,這可能並不重要,但對於企業代碼而言,人們會感到不安和發生訴訟。

  • 爲什麼他們不緩存程序使用的所有整數?

所有整數不能被緩存,因爲內存的影響是巨大的。

  • 爲什麼JVM不總是自動的取消裝箱到原始的?

因爲JVM無法知道你想要什麼。而且,這種改變很容易破壞那些不適合處理這種情況的遺留代碼。

如果JVM在調用==時自動拆箱到原語,這個問題實際上會變得更加混亂。現在你需要記住==總是比較對象引用,除非對象可以被拆箱。這會導致更多奇怪的混亂情況,就像你上面所說的那樣。

甚則擔心太難了這一點,只記得這個規則,而:

NEVER比較==對象,除非你打算通過其引用來比較它們。如果你這樣做,我想不出你遇到問題的場景。

+0

永不說**從不**。你**應該**比較'枚舉值與==。 – 2010-04-08 21:21:44

+0

@Alexander Pogrebnyak - 你是對的,這就是爲什麼我添加了「除非你打算通過他們的引用比較它們」條款。這涵蓋枚舉。我站在我的**從來沒有** :) – 2010-04-08 21:42:26

+2

公平點。然後你仍然有「殺死-9的許可證」:)。 007 out – 2010-04-08 21:51:44

7

你能想象的表現多麼糟糕是,如果每一個Integer進行開銷解決拘留? new Integer也不起作用。

Java語言(不是JVM問題)不能總是自動取消裝箱,因爲爲1.5之前的Java設計的代碼仍然可以工作。

+0

很好的提到1.5之前的代碼。 +1 – Bozho 2010-04-08 19:49:26

+0

提供向後兼容性問題的另一個+1 – Chris 2010-04-08 19:52:01

+0

他們已經通過緩存破壞了舊代碼-128到127 – Pyrolistical 2010-04-08 20:12:20

5

Integer s在字節範圍內是相同的對象,因爲它們被緩存。在字節範圍外的Integer不是。如果所有整數都被緩存,則想象所需的內存。

而且從here

這一切神奇的結果是,你可以在很大程度上忽略int和整數之間的區別,有幾個注意事項。整數表達式可以有一個空值。如果你的程序嘗試autounbox null,它會拋出一個NullPointerException。 ==運算符對Integer表達式執行引用標識比較,並對int表達式進行值相等比較。最後,還有與裝箱和拆箱相關的性能開銷,即使是自動完成的

+0

'[-128,127]之外的整數'可能會或可能不會被執行。 (我認爲原始問題理解發生了什麼,但想知道爲什麼?) – 2010-04-08 19:36:15

+0

我認爲他們不是與太陽的實現。無論如何,這是關於緩存它們和緩存所需的資源。 – Bozho 2010-04-08 19:47:30

4

如果您完全跳過自動裝箱,仍然會出現此問題。

final List<Integer> foo = 
    Arrays.asList(new Integer(1), new Integer(1000)); 
final List<Integer> bar = 
    Arrays.asList(new Integer(1), new Integer(1000)); 

System.out.println(foo.get(0) == bar.get(0)); // false 
System.out.println(foo.get(1) == bar.get(1)); // false 

這是有原因的,爲什麼Eclipse已經自動裝箱爲默認警告:

final List<Integer> foo = 
    Arrays.asList(Integer.valueOf(1), Integer.valueOf(1000)); 
final List<Integer> bar = 
    Arrays.asList(Integer.valueOf(1), Integer.valueOf(1000)); 

System.out.println(foo.get(0) == bar.get(0)); // true 
System.out.println(foo.get(1) == bar.get(1)); // false 

如果你想有一個特定的行爲更明確。

+0

它在Eclipse的副本中默認沒有打開,我沒有改變它。你使用什麼版本?我檢查了3.2和3.4。 – Chris 2010-04-08 19:57:10

+0

@Crhis:Eclipse Gallileo(3.5)Window-> Preferences Java-> Compiler-> Errors/Warnings-> Potential Programming Problems-> Boxing and Unboxing Conversions。也許它默認情況下不是開啓的,但是自從我切換到Java 5以來,它就一直處於開啓狀態。 – 2010-04-08 21:15:33

+0

默認情況下它絕對不是。當我在這裏閱讀時,我只是改變了它。 – 2013-09-06 21:14:38

3

很多人都遇到過這個問題,即使是寫Java書的人也是如此。

Pro Java Programming中,作者講述了使用自動裝箱整數作爲IdentityHashMap中的一個鍵的問題,他在WeakHashMap中使用了自動裝箱的Integer鍵。他使用的示例值大於128,因此他的垃圾收集調用成功。如果有人使用他的例子並使用小於128的值,他的例子將失敗(由於密鑰被永久緩存)。

2

當你寫

foo.get(0) 

編譯不要緊,你是如何創建的列表。它只查看List foo的編譯時類型。所以,如果這是一個列表< Integer>,它會將它視爲列表< Integer>,因爲它應該這樣做,而列表< Integer>的get()總是返回一個Integer。如果你想使用==那麼你必須寫

System.out.println(foo.get(0).intValue() == bar.get(0).intValue()); 

System.out.println(foo.get(0) == bar.get(0)); 

因爲具有完全不同的意義。

相關問題