2010-10-20 16 views
6

開發Java,你一直獲悉,其最好使用List接口作爲列表存儲在該變量的類型來創建一個ArrayList。像這樣爲什麼大多數例子中使用的ArrayList

List<String> myList = new ArrayList<String>(); 

但是,通過查看包中包含的很多android示例,他們使用Class創建了列表。

ArrayList<String> myList = new ArrayList<String>(); 

是否有任何理由這樣做?它是更快,更輕或什麼明確設置類?

+1

「你總是知道最好通過使用List接口來創建一個ArrayList」。有趣的是,我從來沒有聽說過。這樣做的好處是什麼? – 2010-10-20 14:15:55

+5

@Brian如果您儘可能使用接口,代碼將從列表的實現中分離出來。更容易將列表替換爲僅實現列表接口的另一個對象。像一些奇怪的數據庫抽象或其他東西。 – Janusz 2010-10-20 14:20:48

+0

什麼時候有例子總是使用最佳實踐?大多數MySQL/PHP示例都充斥着SQL注入攻擊和其他問題。網絡上的大多數示例都是以儘可能少的代碼獲取儘可能多的功能,或儘可能容易理解的代碼,同時完全忽略最佳實踐,潛在的安全漏洞和錯誤檢查。 – Kibbee 2010-10-20 15:18:27

回答

9

在像Android設計的手機這樣的資源受限環境中,最好避免使用接口,因爲它涉及附加的虛擬函數調用。

+1

這是我一直聽到的解釋,但是,我有興趣看到現實世界的性能差異(從用戶的角度來看)。 – 2010-10-20 14:10:44

+2

我同意。這需要大量的構造函數纔能有所作爲。我會有興趣看到一個實際的應用程序,足以呼籲那裏有一個性能優勢。但後來我再也沒有編程爲Android ... – 2010-10-20 14:17:16

+2

@Daniel Standage:我不認爲使用顯式類型實際上會影響構造函數調用;每次對成員函數的調用都支付「接口稅」(虛函數調用)。構造函數本身幾乎沒有受到影響。不過,我同意,由於最新的智能手機擁有相當強大的處理器以及大量內存,因此需要真正的基準測試才能看出這一點是否真的很重要。 – 2010-10-20 14:23:58

0

除了在Android示例中它們使用更明確的類型之外,沒有太大區別。也許有一些Android方法依賴於接收ArrayList而不僅僅是一個List。

2

儘管可能會有性能優勢,但它幾乎可以確保很小,並且是過早優化的一個例子。

一個更可能的解釋是,這些例子的作者想讓你專注於他(或她)試圖教你的東西。保持變量和它引用相同類型的對象是少一件需要考慮的事情。發佈的代碼示例是一種代碼,如果您確信不需要修改它,那麼使用ArrayList作爲變量類型是完全正確的。

12

我建議閱讀Performance Myths,它解釋了將變量定義爲List或ArrayList的優點和問題。

+0

偉大的鏈接!他們發現,對於沒有JIT的設備,直接使用HashMap而不是Map(HashMap)的速度提高了6%。 – 2010-10-20 14:51:53

+0

而且,以前在文檔中有一個頁面,明確指出Android應該避免使用接口類型(因爲當然,在JIT之前,它比較慢)。無論如何,這就是爲什麼你會看到如此之多的Android例子,直到最近它才被文檔提倡。 – 2010-10-20 22:27:28

10

就我個人而言,我相信「你一直都在學」的東西沒有理解。此:

List<String> myList = new ArrayList<String>(); 

幾乎沒有維護好處。如果您想將其更改爲不同的實現,那麼如果使用實現類型,它仍然只有一條線要更改。然而,完全不同的問題是這樣的:

​​

這裏,使用接口真正重要,因爲它允許誰調用該方法的人使用不同的實現沒有不必更改方法簽名(其他們可能無法做到)。

+0

我完全同意+1 – 2010-10-20 14:48:07

+0

+1現在是結束這個假裝的時候了。我附上了我的解釋。 – irreputable 2010-10-20 15:43:27

+0

'List myList = new ArrayList()'記錄了myList的意圖,因爲代碼希望將myList用作List,而不是特定的ArrayList。 – 2010-10-20 16:13:03

1

編譯運行於List對象上的代碼時,編譯器必須使用接口調用,它比具體類型上的常規虛擬調用更加昂貴。當然,在編譯器看到被實例化的對象並能證明被聲明爲列表的東西總是一個ArrayList <>的代碼片段中,編譯器應該能夠僅僅發出一個定期調用而不是接口調用,但未內聯並在已實例化對象上運行的方法不會從此優化中受益。

無處不在的用於加速虛擬調用的優化,即內聯緩存(通常爲多態內聯緩存或PIC,不要與位置獨立代碼混淆)可從以下觀察中受益:只有單個子類的實例可以通過某種聲明類型的變量。在這種情況下,代碼已經運行儀器了之後,而JIT可以樂觀地猜測,一個List <>對象將永遠只能是一個ArrayList <>,生成陷阱的情況下,猜測是錯誤的,並與該ArrayList告吹撥打電話。

現代處理器上執行的檢查非常快(因爲它們是超標量並具有良好的分支預測),所以你沒有注意到所有這些虛擬呼叫和單一實現接口的通話費用。但它確實可以讓虛擬機工作得更好,檢測,生成和修補所有代碼。

有關熱點穩態運行的服務器軟件,它是無關緊要但對於在移動設備上快速啓動可能有所作爲的 - 我不知道有多好,谷歌的VM。

它一個很好的文章博士懸崖點擊,小(大定製硬件鐵,熱點-deived VM): http://www.azulsystems.com/blog/cliff-click/2010-04-08-inline-caches-and-call-site-optimization

當然,「內聯緩存」在維基百科上。

0

這是一個衆所周知的「設計原理」關於HOWTO做出好的設計,「程序的接口,而不是實現」。像邁克爾博格瓦特說,也許它並不關心使用這個原理與局部變量,但如果你有類型之間的關聯,那麼它是有意義的編程接口而不是實現使用 OOP的好處。實現接口而不是impl允許在運行時動態多態分配。說,

interface IAa { say(); } 
class A implements IAa { public void say() { .. says A ... } } 
class B implements IAa { public void say() { .. says B ... } } 


class App { 

public void someFoo() { 
    IAa myA = new A(); 
    myA.say(); // says A 
    myA = new B(); 
    myA.say(); // says B 

} 
... 
} 

我不認爲,這將在Android的程序是不同的:)

1

Interface x = new Implementation()模式是在是抽象的熱門天真的嘗試。

變量聲明和初始化語句,Type x = new Constructor();,肯定是實現細節的一部分。它不是公共API的一部分(除非它是public final,但List是可變的,因此不合適)

作爲一個實現細節,我們試圖用這個「抽象」來欺騙?儘可能保持特定類型的更好。它是一個數組列表還是一個鏈表?它應該是線程安全還是不安全?選擇對於實現很重要,我們仔細選擇了具體的列表impl。然後,我們將它聲明爲僅僅列表,就好像它不重要,我們不在乎?

將其聲明爲List的唯一正當理由是我懶得輸入。這也涵蓋了如果我需要移動到另一個列表impl我有一個更少的地方來修改的論點。

只有當變量範圍很小時,這個原因纔是合法的,我們可以一目瞭然地看到它的所有用法。否則,請聲明最具體的類型,以便在使用該變量的所有代碼中顯示其性能和語義特徵。

相關問題