簡體   English   中英

Java 8 數組列表。 哪個更快? 在索引 0 處插入一項或使用一項創建新列表並將所有添加到新列表?

[英]Java 8 ArrayList. Which is it faster? Insert one item at index 0 or create new List with one item and addAll to the new List?

讓我們假設我調用第三方 API 並返回一個可變的 N 多對象列表。 該列表可以小到 10 個對象,也可以大到幾千個。 然后我總是想在返回的列表的索引 0 處插入一個對象。 我知道我可以輕松地在索引 0 處調用 add ,但這將是 O(n),因為它會為插入移動每個對象。 我的問題是,使用我計划在開始時插入的項目創建一個新列表,然后在返回的第 3 方 N 多列表中調用該新列表上的 addAll 平均會更快(處理明智)嗎?

這取決於列表實現。 如果您真的不了解第三方為您提供的列表實現,您所能做的就是經驗測試和基准測試。

更有可能的是,他們正在返回你的標准Java列表類型中的一種,而事實上你已經標記了問題arraylist -是,你給什么?

ArrayList.add(index,element)使用System.arrayCopy()將每個移動的元素從索引n復制到n+1 ,然后將新元素寫入其插槽。 這是 O(n),但它確實可能非常快,因為它將使用高度優化的系統memmove例程一次移動整個 RAM 塊。 (請參閱為什么 memcpy() 和 memmove() 比指針增量快? )。

此外,如果您的額外元素使列表的大小超過分配的后備數組的大小,Java 將創建一個新數組並將整個數組arraycopy到那里。

請記住,您只是在復制對象引用,而不是整個對象,因此對於 1000 個元素,您正在復制(在 64 位機器上的最壞情況)64 位 * 1000 == 8 KB 的 RAM。

盡管如此,對於非常龐大的列表,它所花費的時間可能會變得很重要。 插入鏈表更便宜(在開始或結束時應該是 O(1))

您可以通過編寫/查找一個 List 實現來使其成為對任意 List 實現的 O(1) 操作,該實現只是現有列表的一個包裝器。 例如:

public class HeadedList<T> extends AbstractList<T> {

    private final List tail;

    public HeadedList(T head, List tail) {
       this.head = head;
       this.tail = tail;
    }

    public T get(int index) {
        return index == 0 ? head : tail.get(index - 1);
    }

    public int size() {
        return tail.size() + 1;
    }
}

(注意,如果您使用 Lisp/Clojure/etc 等語言工作,您會非常習慣以這種方式思考列表)

但是,只有在基准測試顯示真正的性能問題是由列表構建引起的時候才需要考慮這個問題。

如果返回的 List impl 是 ArrayList,則兩個選項相同:O(n)。
如果返回的 impl 是 LinkedList,則在頭部插入是 O(1)。

總是有一個 O(1) 選項:創建一個 List 包裝類,它由返回的列表支持,但允許通過在內部存儲插入的元素在頭部插入。 您必須創建一個自定義迭代器來迭代插入的元素,然后委托給列表。 大多數方法都需要類似的定制。

如果它只有 1000 個左右的元素,我不會打擾,除非您的應用程序是完整的,並且您已經確定在此操作中存在可衡量且足夠嚴重的性能問題。

如果您在頭部插入多個元素,那么您需要點擊一次以創建一個 LinkedList,然后每次插入都是 O(1),但由於您只有 1 個要插入,所以不要打擾。

KISS:只需將元素插入返回的列表中。 我相信它會足夠快,而且很可能比圖書館快得多。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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