Java中LinkedList的remove方法真的耗時O(1)嗎?


這個問題其實來源於Leetcode的一道題目,也就是上一篇日志 LRU Cache。在使用LinkedList超時后,換成ArrayList居然AC了,而問題居然是在於List.remove(Object o)這個方法。

我們知道,鏈表和數組相比,最主要的特點就是add和remove的操作是O(1)的。Java中的鏈表一般使用LinkedList這個類型,數組一般使用ArrayList。它們同時implements了List這個interface,所以都有remove(int index)和remove(Object o)這兩個方法。

普通意義上認為鏈表的remove操作是O(1)的,是因為對於某個給定的節點node,可以將它的前置節點的next直接置為node的下一個。而數組,則需要刪除index處的元素,再將后面n個元素前移,所以需要O(n)的時間。

但是,在Java中果真如此嗎?

細看JDK的源碼,就可以發現,LinkedList的remove(int index)和remove(Object o)這兩個方法都做不到O(1)的時間,而是O(n)。這是因為上面說的數據結構中的O(1)時間,是對於某個已經確定的節點。而LinkedList中,首先必須通過一個循環,找到第一個出現的Object o,或者走到index這個位置,再進行操作。也就是,有一個get的過程。

這時,雖然ArrayList的remove(int index)和remove(Object o)也是O(n)時間,但是移動耗費的時間遠比LinkedList中往后尋址來的快得多,特別是元素很多的時候。JDK的源碼里,這個操作是用System.arraycopy()來做的。所以,這時,LinkedList的最為坑爹的地方,也是最令人不解的地方就出現了——remove的操作居然比ArrayList還慢,而且慢的多!

而且,LinkedList需要內部維護一個數據結構,JDK 6中叫Entry,JDK 7中叫Node,這需要很多額外的內存。所以,除非急切需要LinkedList的Deque功能,任何情況下都應該使用ArrayList。其實,即使要用Deque,也有ArrayDeque。

所以,有時面試會問,在一個LinkedList list的遍歷for循環中,不斷執行remove(i)操作,時間復雜度是多少?其實是O(n^2),而不是O(n)。但是,如果使用iterator,it.remove()的時間復雜度就是O(1)了,因為這時元素已經給定。並且,for循環中進行remove(i)操作是要影響下標的。remove過后每次i都必須i--。使用iterator可以有效避免這個問題。這里可以看到,雖然for循環比較直觀,但是有時iterator還是非常好的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM