Neden Yığın Üzerinde Deque kullanmalıyım?


157

StackKullanım durumum için bir veri yapısına ihtiyacım var . Öğeleri veri yapısına zorlayabilmeliyim ve yalnızca Yığındaki son öğeyi almak istiyorum. Yığın için javadoc diyor:

Deque arabirimi ve bu sınıfa göre kullanılması gereken uygulamaları tarafından daha eksiksiz ve tutarlı bir LIFO yığın operasyonları seti sağlanır. Örneğin:

Deque<Integer> stack = new ArrayDeque<>();

Kesinlikle burada senkronize davranış istemiyorum çünkü bu veri yapısını yerel bir yöntem için kullanacağım. Bunun dışında neden tercih etmeliyiz Dequeüzerinde Stackburada?

Not: Deque'den Javadoc şöyle diyor:

Deques, LIFO (Last-In-First-Out) yığınları olarak da kullanılabilir. Bu arabirim, eski Stack sınıfının yerine kullanılmalıdır.


1
Daha fazla pişmiş yöntemler veya daha doğrusu "daha eksiksiz ve tutarlı bir dizi" sağlar, bu da bunlardan yararlanırsanız yazmanız gereken kod miktarını azaltır mı?

Yanıtlar:


190

Bir kere miras açısından daha mantıklı. Aslında Stackuzanır Vectorbana göre gerçekten garip. Java'nın başlarında, kalıtım IMO'yu aşırı kullanmıştı - Propertiesbaşka bir örnek.

Benim için, alıntıladığınız dokümanlardaki önemli kelime tutarlıdır . Dequebir koleksiyonun, yinelemenin vb. başlangıcından veya sonundan öğe getirme / ekleme / kaldırma ile ilgili bir dizi işlemi ortaya koyar ve hepsi bu kadar. Pozisyon ile öğeye erişmek için hiçbir şekilde kasten yok Stackortaya çıkarır çünkü o bir alt sınıfı var Vector.

Oh, ve ayrıca Stackarayüzü yok, bu yüzden Stackoperasyonlara ihtiyacınız olduğunu biliyorsanız , genellikle iyi bir fikir olmayan belirli bir somut sınıfa girersiniz.

Ayrıca yorumlarda belirtildiği gibi Stackve Dequeters yineleme siparişleri var:

Stack<Integer> stack = new Stack<>();
stack.push(1);
stack.push(2);
stack.push(3);
System.out.println(new ArrayList<>(stack)); // prints 1, 2, 3


Deque<Integer> deque = new ArrayDeque<>();
deque.push(1);
deque.push(2);
deque.push(3);
System.out.println(new ArrayList<>(deque)); // prints 3, 2, 1

Deque.iterator () için JavaDocs'da da açıklanmıştır :

Uygun bir sırayla bu deque içindeki öğeler üzerinde bir yineleyici döndürür. Elemanlar ilk (baş) ile son (kuyruk) arasında sırayla döndürülecektir.


10
ArrayDeque javadoc'u "Bu sınıfın yığın olarak kullanıldığında Stack'tan daha hızlı ve kuyruk olarak kullanıldığında LinkedList'ten daha hızlı olması muhtemeldir." ..Bunu yığın olarak mı yoksa kuyruk olarak mı kullanacağımı nasıl belirleyebilirim?
Geek

23
@Geek: Yapmıyorsun. Mesele şu ki, kuyruk davranışı istiyorsanız , kullanabilirsiniz LinkedList, ancak ArrayDequeue(genellikle) daha hızlı olacaktır. Yığın davranışı istiyorsanız , kullanabilirsiniz Stackancak ArrayDeque(genellikle) daha hızlı olacaktır.
Jon Skeet

7
Yine de soyutlama açısından daha az mantıklı değil mi? Yani, her iki çözüm de soyutlama açısından gerçekten iyi değil, çünkü tekrar Stackmaruz kalma sorunu var, ancak bir yığın veri yapısı istersem, itme, pop ve peek gibi yöntemleri çağırabiliyorum. yığının diğer ucuyla yapın.
PeteyPabPro

4
@JonSkeet ayrıca Stack'in yineleyicisi yanlıştır, çünkü yukarıdan aşağıya yerine aşağıdan yukarıya doğru yinelenir. stackoverflow.com/questions/16992758/…
Pavel

1
@PeteyPabPro: Haklısın. Dequeue'yu yığın olarak kullanmak, kalıtımsal Yığın Vektör yöntemleri olarak LIFO dışı kullanıma izin verir. Sorunu ve (kapsülleme ile) çözümün bir açıklama burada bulunabilir: baddotrobot.com/blog/2013/01/10/stack-vs-deque
Rics

4

İşte Stack sınıfının açıklamasında belirtilen tutarsızlık yorumum.

Burada Genel Amaçlı Uygulamalara bakarsanız, set, harita ve listenin uygulanmasında tutarlı bir yaklaşım olduğunu göreceksiniz.

  • Set ve harita için karma harita ve ağaçlarla 2 standart uygulamamız var. Birincisi en çok kullanılır ve ikincisi sıralı bir yapıya ihtiyacımız olduğunda kullanılır (ve aynı zamanda kendi arayüzünü de uygular - SortedSet veya SortedMap).

  • Tercih edilen beyan tarzını, buradaSet<String> set = new HashSet<String>(); nedenleri görmek gibi kullanabiliriz .

Ancak Stack sınıfı: 1) kendi arayüzü yoktur; 2) Vector sınıfının bir alt sınıfıdır - yeniden boyutlandırılabilir diziye dayanır; öyleyse yığının bağlantılı liste uygulaması nerede?

Deque arayüzünde iki uygulama (yeniden boyutlandırılabilir dizi - ArrayDeque; bağlantılı liste - LinkedList) dahil olmak üzere böyle sorunlarımız yok.


2

Yığın Üzerinde Dequeue kullanmanın bir başka nedeni Dequeue, Yığın yapmadığı halde LIFO konseptini uygulayarak akışları listeye dönüştürme özelliğini kullanabilmesidir.

Stack<Integer> stack = new Stack<>();
Deque<Integer> deque = new ArrayDeque<>();

stack.push(1);//1 is the top
deque.push(1)//1 is the top
stack.push(2);//2 is the top
deque.push(2);//2 is the top

List<Integer> list1 = stack.stream().collect(Collectors.toList());//[1,2]

List<Integer> list2 = deque.stream().collect(Collectors.toList());//[2,1]

2

Deque'in Stack'tan daha iyi olmasının birkaç nedeni:

Nesneye yönelik tasarım - Kalıtım, soyutlama, sınıflar ve arayüzler: Stack bir sınıf, Deque bir arayüzdür. Yalnızca bir sınıf genişletilebilirken, Java'da tek bir sınıf (birden çok tür mirası) tarafından herhangi bir sayıda arabirim uygulanabilir. Deque arabirimini kullanmak, beton Stack sınıfına ve atalarına olan bağımlılığı ortadan kaldırır ve size daha fazla esneklik sağlar, örneğin farklı bir sınıfı genişletme veya Deque'nin farklı uygulamalarını (LinkedList, ArrayDeque gibi) değiştirme özgürlüğü.

Tutarsızlık: Yığın, öğeye dizine göre erişmenizi sağlayan Vector sınıfını genişletir. Bu, Stack'in gerçekte yapması gereken şeyle tutarsızdır, bu nedenle Deque arabirimi tercih edilir (bu tür işlemlere izin vermez) - izin verilen işlemleri bir FIFO veya LIFO veri yapısının izin vermesi ile tutarlıdır.

Performans: Stack'in genişlettiği Vector sınıfı temel olarak bir ArrayList öğesinin "iş parçacığı için güvenli" sürümüdür. Senkronizasyonlar, potansiyel olarak uygulamanızda önemli bir performans artışına neden olabilir. Ayrıca, (# 2'de belirtildiği gibi) gereksiz işlevselliğe sahip diğer sınıfları genişletmek, nesnelerinizi şişirir ve potansiyel olarak çok fazla ek bellek ve performans yükü oluşturur.


-1

Benim için bu özel nokta eksikti: Stack, Vector'dan türetildiği gibi Threadsafe'dir, oysa en deque uygulamaları yoktur ve bu nedenle yalnızca tek bir iş parçacığında kullanırsanız daha hızlıdır.


grep "Bunun dışında"
Pacerier

-1

Performans bir sebep olabilir. Kullandığım algoritma, Stack'i Deque ile değiştirerek 7.6 dakikadan 1.5 dakikaya indi.


grep "Bunun dışında"
Pacerier

-6

Deque, hem kafadan hem de kuyruktan eleman almak istediğiniz durumdur. Basit bir yığın istiyorsanız, deque için gitmeye gerek yok.


1
lütfen sorudaki son uygulamaya bakınız. Javadoc, Deques'ın kullanılması gerektiğini ve nedenini bilmek istediğimi söylüyor?
Geek

2
Deque tercih edilir, çünkü ortadaki öğelere erişemezsiniz. Çift uçlu, bu yüzden FIFO için FILO olabilir.
Thufir

Söylemek istedikleri sınıfın Stack işlemleri açık yöntemler olarak destekliyor olduğunu düşünüyorum. Belgelerine ve yöntemlerine bakın. Bir Yığın uygulanması için açık bir desteği vardır.
Abhishek Nandgaonkar
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.