ArrayDeque neden LinkedList 'den daha iyi?


161

Her ikisi de Deque arabirimini uygularken Java'nın ArrayDeque neden Java'nın LinkedList daha iyi anlamaya çalışıyorum .

Kodlarında ArrayDeque kullanan birini görmüyorum. Birisi ArrayDeque'in nasıl uygulandığına daha fazla ışık tutarsa, yardımcı olacaktır.

Anlarsam, onu kullanmaktan daha emin olurum. JDK uygulamasını kafa ve kuyruk referanslarını yönetme şekli konusunda net bir şekilde anlayamadım.


5
Günler önce yaptığım bu sorunun cevabına bakın: stackoverflow.com/questions/6129805/…
Renato Dinhani

1
Jdk'nizin yüklü olduğu klasörde bir dosya var src.zip. Java sınıflarının kaynak koduna sahip arşivdir. Java sınıflarının nasıl çalıştığını daha iyi anlamak için bu sınıfların yapısını ve içini incelemenizi önemle tavsiye ederim.

Yanıtlar:


155

Bağlantılı yapılar muhtemelen her elemanda bir önbellek özledim ile yinelenen en kötü yapıdır. Üstelik daha fazla bellek tüketiyorlar.

Her iki ucu da eklemeniz / kaldırmanız gerekiyorsa, ArrayDeque bağlantılı bir listeden önemli ölçüde daha iyidir. Çevrimsel bir kuyruk için her bir öğeye rastgele erişim de O (1) 'dir.

Bağlantılı listenin tek daha iyi işlemi, yineleme sırasında geçerli öğeyi kaldırmaktır.


57
Akılda tutulması gereken bir başka fark: LinkedList null öğeleri desteklerken ArrayDeque desteklemez.
Luke Usherwood

15
Ayrıca başka bir küçük dezavantaj (gerçek zamanlı uygulamalar için), bir push / add işleminde, boyutunu iki katına çıkarmak ve tüm verileri kopyalamak zorunda olduğu için ArrayDeque'in dahili dizisi dolduğunda biraz daha fazla zaman almasıdır.
Andrei I

4
@AndreiI, bu hikayenin sadece bir tarafı. Gerçek zamanlı uygulama için yineleme maliyetlerini ve gerekli kapasiteyi önceden konumlandırabilme yeteneğini hariç tutsanız bile, GC'nin LinkedList'in tamamını yinelemesi gerekebilir. Temel olarak maliyetleri (önyükleme için daha yüksek olan) GC'ye taşıyorsunuz.
bestsss

3
@DavidT. b / c, serbest düğümün GC maliyetlerini içerir, kafanın atanması ayrıca kart işaretlemeyi de gerektirebilir (LinkedList zaten kiralanmış gendeyse GC için) ... ve bu ekstra dolaylamanın üstündedir (önbellek- özledim) öğeyi döndürmek ve yeniden bağlamak için.
bestsss

1
Ayrıca, LinkedListuygular Listise ArrayDequeyapmaz. Bu araçlar LinkedListyöntemleri var gibi indexOfya remove(int)da ArrayDequesahip değildir. Bazen önemli olabilir.
ZhekaKozlov

60

Ana performans darboğazının LinkedList, deque'in herhangi bir ucuna her bastığınızda, sahnenin arkasında, uygulamanın esas olarak JVM / OS'yi içeren yeni bir bağlantılı liste düğümü tahsis ettiği ve bu pahalı olduğu gerçeğine inanıyorum. Ayrıca, herhangi bir uçtan çıktığınızda, iç düğümler LinkedListçöp toplama için uygun hale gelir ve bu daha çok sahnenin arkasında çalışır. Ayrıca, bağlı liste düğümleri burada ve orada ayrıldığından, CPU önbelleğinin kullanımı fazla fayda sağlamaz.

İlgilenebilirse, amortize edilmiş sabit süreye bir öğe eklemenin (ekleyerek) ArrayListveya ArrayDequeispatlanmış sabit zamanda çalıştığına dair bir kanıtım var ; bakın bu .


26

ArrayDeque Java 6'da yenidir, bu yüzden çok sayıda kod (özellikle önceki Java sürümleriyle uyumlu olmaya çalışan projeler) kullanmaz.

Bazı durumlarda "daha iyi" olur çünkü her öğenin ekleyebileceği bir düğüm ayırmazsınız; bunun yerine tüm öğeler dev bir dizide saklanır ve dolduğunda yeniden boyutlandırılır.


17

A eleştiren tüm insanlar , Java'da kullanan LinkedListdiğer herkesin Listmuhtemelen kullandığını ArrayListve LinkedListçoğu zaman Java 6'dan önce olduğu için ve çoğu kitapta başlangıç ​​olarak öğretilenler olduğunu düşünün .

Ancak bu, körü körüne LinkedListya da ArrayDequetarafına bakacağım anlamına gelmez . Bilmek istiyorsanız, Brian tarafından yapılan aşağıdaki karşılaştırmaya bir göz atın .

Test kurulumu şunları dikkate alır:

  • Her test nesnesi 500 karakterlik bir String'dir. Her Dize bellekteki farklı bir nesnedir.
  • Test dizisinin boyutu testler sırasında değişecektir.
  • Her dizi boyutu / Kuyruk uygulama kombinasyonu için 100 test çalıştırılır ve test başına ortalama süre hesaplanır.
  • Her test, her kuyruğu tüm nesnelerle doldurmaktan sonra hepsini kaldırmaktan oluşur.
  • Süreyi milisaniye cinsinden ölçün.

Test sonucu:

  • 10.000 öğenin altında, LinkedList ve ArrayDeque testlerinin ortalaması 1 ms alt düzeydedir.
  • Veri setleri büyüdükçe, ArrayDeque ve LinkedList ortalama test süresi arasındaki farklar artar.
  • 9.900.000 elementin test boyutunda LinkedList yaklaşımı, ArrayDeque yaklaşımından ~% 165 daha uzun sürdü.

Grafik:

resim açıklamasını buraya girin

Paket servisi:

  • Gereksiniminiz 100 veya 200 öğe saklıyorsa, Kuyruklardan herhangi birini kullanarak fazla bir fark yaratmaz.
  • Ancak, mobil cihazlarda geliştiriyorsanız , listenin katı bellek kısıtlaması nedeniyle olması gerekebilecek maksimum kapasite tahminini ArrayListveya ArrayDequeiyi bir tahminini kullanmak isteyebilirsiniz .
  • Bir çok kod var, özellikle LinkedListkullanmaya karar verirken dikkatli bir şekilde lastik sırtı kullanılarak yazılmış, ArrayDequeçünkü özellikle arayüzü uygulamazList (bu nedenle yeterince büyük olduğunu düşünüyorum). Kod tabanınızın Liste arabirimiyle kapsamlı bir şekilde konuşması, büyük olasılıkla ve bir ile atlamaya karar verebilirsiniz ArrayDeque. Dahili uygulamalar için kullanmak iyi bir fikir olabilir ...

Bu kıyaslama, bağlantılı liste çöpünün neden olduğu GC süresini nasıl yakalar?
06

3

ArrayDeque ve LinkedList , Deque arabirimini uyguluyor ancak uygulama farklı.

Temel farklılıklar:

  1. ArrayDeque sınıf içinde yeniden boyutlandırılabilir dizi uygulamasıdır deque arayüzü ve LinkedList sınıf listesi uygulamasıdır

  2. NULL öğeleri LinkedList'e eklenebilir ancak ArrayDeque içine eklenemez

  3. ArrayDeque , her iki uçta ekleme ve kaldırma işlemi için LinkedList'ten daha etkilidir ve LinkedList uygulaması, yineleme sırasında geçerli öğeyi kaldırmak için etkilidir

  4. LinkedList uygulama daha fazla bellek tüketir ArrayDeque

Dolayısıyla, NULL öğeleri desteklemeniz gerekmiyorsa & daha az bellek arıyorsanız && her iki uçta öğe ekleme / kaldırma verimliliği, ArrayDeque en iyisidir

Daha fazla ayrıntı için belgelere bakın .


13
"Bağlantılı listede, son öğeyi bulmak O (N) sürecektir." tam olarak doğru değil. LinkedList, iki bağlantılı bir liste olarak uygulandığından, son öğeyi ( header.previous.element) almak için listeyi geçmeniz gerekmez . "Bellek verimliliği" iddiası da desteklenebilir, çünkü destek dizisi her zaman bir sonraki güç 2 olarak yeniden boyutlandırılır.
Clément MATHIEU

5
"Son elemanın bulunması O (N) alacaktır" YANLIŞ. Bağlantılı liste son düğüme bir başvuru tutar ve LinkedList.descendingIterator () get bu düğüme. Böylece O (1) performansı elde ediyoruz. Bakınız: kahve içeren programlama.wordpress.com/2018/04/23/… (böylece aşağı oylama).
Leo Ufimtsev

1
IteratorSon öğeye erişmek için a kullandığınızda , işlem her iki sınıf için de O (N) olur. Ortak Dequearabirimi kullandığınızda, her iki sınıf için de son öğeye erişmek O (1) olur. Hangi bakış açısına sahip olduğunuzdan bağımsız olarak, O (1) ArrayDequeve O (N) LinkedListözelliklerini aynı anda ilişkilendirmek yanlıştır.
Holger

Tüm önerileriniz alınır ve içerik düzeltilir
Ravindra babu

1

Her ne kadar ArrayDeque<E>ve LinkedList<E>her iki uygulamaya sahip Deque<E>Arayüzü ama ArrayDeque temelde nesne dizi kullanan E[]genellikle baş ve kuyruk elemanları bulmak için dizini kullanır, böylece onun Nesne içine öğeler tutmak için.

Tek kelimeyle, Deque gibi çalışır (tüm Deque'nin yöntemiyle), ancak dizinin veri yapısını kullanır. Hangisinin daha iyi olduğu konusunda, bunları nasıl ve nerede kullandığınıza bağlıdır.


-1

Bu her zaman böyle değildir.

Örneğin, aşağıdaki durumda leetcode 103'e göre linkedlistdaha iyi performans gösterir ArrayDeque.

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
        List<List<Integer>> rs=new ArrayList<>();
        if(root==null)
            return rs;
        // 👇 here ,linkedlist works better
        Queue<TreeNode> queue=new LinkedList<>();
        queue.add(root);
        boolean left2right=true;
        while(!queue.isEmpty())
        {
            int size=queue.size();
            LinkedList<Integer> t=new LinkedList<>();
            while(size-->0)
            {
                TreeNode tree=queue.remove();
                if(left2right)  
                    t.add(tree.val);
                else
                    t.addFirst(tree.val);
                if(tree.left!=null)
                {
                    queue.add(tree.left);
                }
                if(tree.right!=null)
                {
                    queue.add(tree.right);
                }
            }
            rs.add(t);
            left2right=!left2right;
        }
        return rs;
    }
}

-5

Bir öğeye erişmek için ArrayDeque için zaman karmaşıklığı O (1) ve LinkList için ise son öğeye erişmek için O (N). ArrayDeque iş parçacığı için güvenli değildir, bu nedenle birden çok iş parçacığından erişebilmeniz için manuel eşitleme gereklidir ve daha hızlı olurlar.


3
Java'lara atıfta LinkedListbulunuyorsanız Collection, iki kez bağlanır ve kafaya ve kuyruğa hızlı erişime sahiptir, bu nedenle son öğeye erişim de O (1) alır.
Maurice

LinkedList'teki son öğeye erişmek O (N) değil. DescendingIterator () kullanırsanız, O (1) 'de gerçekleştirilir. Bkz. Kahveciprogramming.wordpress.com/2018/04/23/… (böylece aşağı oy).
Leo Ufimtsev

1
Her iki sınıf da iş parçacığı için güvenli değildir. Ve cümlenin başlangıcı “elle senkronizasyon gereklidir” ve sonu “daha ​​hızlıdır” arasında bir bağlantı yoktur.
Holger
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.