兩個小問題深入淺出List的效能問題

山有木xi發表於2023-02-25

最近在開發中發現,在使用時,很多外掛、庫是需要我們傳入 List 資料的,而一般都會提供資料重置方法,而我發現大部分庫的資料重置方法採用的是先 clear addall ,就想到 問題1:addAll的效能會更好嗎?

我的推測是,在 resetData 時,如果使用 addAll 就必須先 clear ,這樣肯定會損失一定的效能的,如果直接重新 new 一個的話,似乎效能方面會更好,而集合的 addAll 此方法按照指定  collection  的迭代器所返回的元素順序,將該  collection  中的所有元素新增到此列表的尾部。

讓我們來看看實際的測試結果,先看看addAll的情況,程式碼如下:

private fun test() {
    val list= mutableListOf<String>()
    for (i in 1 .. 1000000){
        list.add("")
    }
    Timber.e("初始時間===${monthDayFormat.format(System.currentTimeMillis())}")
    testList.clear()
    testList.addAll(list)
    Timber.e("結束時間===${monthDayFormat.format(System.currentTimeMillis())}")
}

執行結果如下:

再來看看直接覆蓋的情況,程式碼如下:

private fun test() {
    val list= mutableListOf<String>()
    for (i in 1 .. 1000000){
        list.add("")
    }
    Timber.e("初始時間===${monthDayFormat.format(System.currentTimeMillis())}")
    testList.clear()
    testList=list
    Timber.e("結束時間===${monthDayFormat.format(System.currentTimeMillis())}")
}

執行結果如下:


  可以看到,在資料量越大的情況下,直接覆蓋比addAll要快很多,但是這樣子就又有一個新的問題,在很多情況下,特別是第三方庫中的List,普遍使用final型別的情況就不能直接覆蓋。這個時候就想到 問題2:final的好處是什麼

  可以查到的資料顯示,使用關鍵字 final 將有助於編譯器靜態最佳化程式碼,這最終可能導致更快的程式碼,但是他能提高的速度遠遠沒有到達非常快的速度,讓我們看下面的程式碼

public class FinalTest {
    public static final int N_ITERATIONS = 1000000;
    public static String testFinal() {
        final String a ="a";
        final String b ="b";
        return a + b;
    }
    public static String testNonFinal() {
        String a ="a";
        String b ="b";
        return a + b;
    }
    public static void main(String[] args) {
        long tStart, tElapsed;
        tStart = System.currentTimeMillis();
        for (int i = 0; i < N_ITERATIONS; i++)
            testFinal();
        tElapsed = System.currentTimeMillis() - tStart;
        System.out.println("Method with finals took" + tElapsed +" ms");
        tStart = System.currentTimeMillis();
        for (int i = 0; i < N_ITERATIONS; i++)
            testNonFinal();
        tElapsed = System.currentTimeMillis() - tStart;
        System.out.println("Method without finals took" + tElapsed +" ms");
    }
}

看看他的執行結果:

Method with finals took 5 ms
Method without finals took 273 ms

  在高度最佳化的的程式下可能會有影響,到這個時候好像就沒有什麼問題了,但是我又突然想到開發的一個知識點 List集合的區別

       Java 集合就像一個容器,主要由 2 大體系構成,分別是 Collection 體系和 Map 體系,其中 Collection Map 分別是 2 大體系中的頂層介面 Collection 主要有三個子介面,分別為 List( 列表 ) Set( ) Queue( 佇列 ) 。其中, List Queue 中的元素有序可重複,而 Set 中的元素無序不可重複, List 中主要有 ArrayList LinkedList 兩個實現類。

  其中 ArrayList 底層透過陣列實現,隨著元素的增加而動態擴容, ArrayList 實現 RandomAccess ,獲得了快速隨機訪問儲存元素的功能, ArrayList 的特點是容量不固定,隨著容量的增加而動態擴容

  而 LinkedList 底層透過連結串列來實現,隨著元素的增加不斷向連結串列的後端增加節點,它的特點是讀取的速度慢寫入的速度快





來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69917874/viewspace-2937032/,如需轉載,請註明出處,否則將追究法律責任。

相關文章