[英]Why is list.size()>0 slower than list.isEmpty() in Java?
為什么list.size()>0
在 Java 中比list.isEmpty()
慢? 換句話說,為什么isEmpty()
比size()>0
更可取?
當我查看ArrayList
中的實現時,看起來速度應該是一樣的:
ArrayList.size()
/**
* Returns the number of elements in this list.
*
* @return the number of elements in this list
*/
public int size() {
return size;
}
ArrayList.isEmpty()
/**
* Returns <tt>true</tt> if this list contains no elements.
*
* @return <tt>true</tt> if this list contains no elements
*/
public boolean isEmpty() {
return size == 0;
}
如果我們只是編寫一個簡單的程序來獲取這兩種方法所花費的時間,那么 case size()
isEmpty()
,為什么會這樣呢?
這是我的測試代碼;
import java.util.List;
import java.util.Vector;
public class Main {
public static void main(String[] args) {
List l=new Vector();
int i=0;
for(i=0;i<10000;i++){
l.add(new Integer(i).toString());
}
System.out.println(i);
Long sTime=System.nanoTime();
l.size();
Long eTime=System.nanoTime();
l.isEmpty();
Long eeTime=System.nanoTime();
System.out.println(eTime-sTime);
System.out.println(eeTime-eTime);
}
}
這里eTime-sTime>eeTime-eTime
在所有情況下。 為什么?
對於ArrayList
,是的——你是正確的,操作需要(大致)相同的時間。
對於List
的其他實現——例如,一個簡單的鏈表*——計算大小可能需要很長時間,而您實際上只關心它是否大於零。
因此,如果您絕對知道列表是ArrayList
的實現並且永遠不會改變,那么這並不重要; 但:
size() == 0
仍然不比isEmpty()
快,因此沒有令人信服的理由使用前者。isEmpty()
更清晰地定義了您真正關心和正在測試的內容,因此使您的代碼更容易理解。 * 我最初在這里編寫 LinkedList ,隱式引用java.util.LinkedList
,盡管該特定實現確實顯式存儲了它的大小,使size()
在這里成為 O(1) 操作。 一個簡單的鏈表操作可能不會這樣做,並且在更一般的意義上, List
的實現沒有效率保證。
您的測試代碼有缺陷。
只需顛倒順序,即首先調用 isEmpty 和 size > 0 秒,您將得到相反的結果。 這是由於類加載、緩存等造成的。
.size() 必須查看整個列表,而 .isEmpty() 可以在第一個列表中停止。
顯然依賴於實現,但正如之前所說,如果您不需要知道實際大小,為什么還要計算所有元素呢?
你說:
這里
eTime-sTime>eeTime-eTime
在所有情況下為什么?
首先,這可能是因為您的測試代碼。 您不能同時測試調用 l.size() 和 l.isEmpty() 的速度,因為它們都查詢相同的值。 很可能調用 l.size() 已將列表的大小加載到 cpu 緩存中,因此調用 l.isEmpty() 會快得多。
你可以嘗試在兩個單獨的程序中調用 l.size() 幾百萬次和 l.isEmpty() 幾百萬次,但理論上編譯器可以優化所有這些調用,因為你實際上並沒有做任何事情結果。
無論如何,兩者之間的性能差異可以忽略不計,尤其是在進行比較后,您需要查看列表是否為空( l.size() == 0
)。 生成的代碼很可能看起來幾乎完全相似。 正如其他一些海報所指出的,在這種情況下,您希望優化可讀性,而不是速度。
編輯:我自己測試過。 這幾乎是一個折騰。 Vector
上使用的size()
和isEmpty()
在長期運行中給出了不同的結果,兩者都沒有始終如一地擊敗另一個。 在ArrayList
上運行時, size()
似乎更快,但幅度不大。 這很可能是由於對Vector
的訪問是同步的,因此在嘗試對這些方法的訪問進行基准測試時,您真正看到的是同步開銷,這可能非常敏感。
這里要注意的是,當您嘗試優化執行時間相差幾納秒的方法調用時,那么您做錯了。 首先掌握基礎知識,例如在應該使用long
的地方使用Long
。
對鏈表中的項目進行計數可能會非常慢。
鑒於這兩種實現,速度應該是相同的,這是真的。
但到目前為止,這些並不是這些方法的唯一可能實現。 例如,原始鏈表(不單獨存儲大小的鏈表)可以比size()
調用更快地回答isEmpty()
。
更重要的是: isEmpty()
准確地描述了您的意圖,而size()==0
是不必要的復雜(當然不是非常復雜,但應該避免任何不必要的復雜性)。
根據 PMD(基於靜態規則集的 Java 源代碼分析器),isEmpty() 是首選。 您可以在此處找到 PMD 規則集。 搜索“UseCollectionIsEmpty”規則。
http://pmd.sourceforge.net/rules/design.html
據我說,它還有助於保持整個源代碼的一致性,而不是一半的人使用 isEmpty() 而其余的人使用 size()==0。
一般不可能說哪個更快,因為這取決於您使用的接口List
的實現。
假設我們正在談論ArrayList
。 查找ArrayList
的源代碼,您可以在 JDK 安裝目錄的src.zip文件中找到它。 方法isEmpty
和size
的源代碼如下所示(Sun JDK 1.6 update 16 for Windows):
public boolean isEmpty() {
return size == 0;
}
public int size() {
return size;
}
您可以很容易地看到isEmpty()
和size() == 0
這兩個表達式都將歸結為完全相同的語句,因此一個肯定不會比另一個快。
如果您對它在接口List
的其他實現中的工作方式感興趣,請自行查找源代碼並找出答案。
list.size() 將通過遍歷一個又一個索引來計算元素的數量。 list.isEmpty() 將檢查第一個元素並確定它不為空並立即給出結果。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.