簡體   English   中英

為什么 list.size()>0 在 Java 中比 list.isEmpty() 慢?

[英]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的實現並且永遠不會改變,那么這並不重要; 但:

  1. 這是將自己束縛在特定實現上的不好的編程習慣。
  2. 如果幾年后隨着代碼重組而發生變化,測試將表明“它有效”,但運行效率比以前低了。
  3. 即使在最好的情況下, size() == 0仍然不比isEmpty(),因此沒有令人信服的理由使用前者。
  4. isEmpty()更清晰地定義了您真正關心和正在測試的內容,因此使您的代碼更容易理解。

* 我最初在這里編寫 LinkedList ,隱式引用java.util.LinkedList ,盡管該特定實現確實顯式存儲了它的大小,使size()在這里成為 O(1) 操作。 一個簡單的鏈表操作可能不會這樣做,並且在更一般的意義上, List的實現沒有效率保證。

您的測試代碼有缺陷。

只需顛倒順序,即首先調用 isEmpty 和 size > 0 秒,您將得到相反的結果。 這是由於類加載、緩存等造成的。

對不起,你的基准有缺陷。 看一下Java 理論和實踐:有缺陷的微基准的剖析,以獲得有關如何處理基准的一般描述。


更新:為了獲得適當的基准,您應該查看Japex

.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文件中找到它。 方法isEmptysize的源代碼如下所示(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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM