Mengapa saya harus menggunakan Deque over Stack?

157

Saya membutuhkan Stackstruktur data untuk kasus penggunaan saya. Saya harus bisa mendorong item ke dalam struktur data dan saya hanya ingin mengambil item terakhir dari Stack. The javadoc untuk Stack mengatakan:

Serangkaian operasi stack LIFO yang lebih lengkap dan konsisten disediakan oleh antarmuka Deque dan implementasinya, yang harus digunakan sebagai preferensi untuk kelas ini. Sebagai contoh:

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

Saya pasti tidak ingin perilaku tersinkronisasi di sini karena saya akan menggunakan datastructure ini ke metode lokal. Terlepas dari ini, mengapa saya lebih suka Dequedi Stacksini?

PS: Javadoc dari Deque mengatakan:

Deques juga dapat digunakan sebagai tumpukan LIFO (Last-In-First-Out). Antarmuka ini harus digunakan sebagai preferensi untuk kelas Stack legacy.

Kutu buku
sumber
1
Ini memberikan lebih banyak metode baked-in, atau lebih tepatnya "set yang lebih lengkap dan konsisten" dari mereka, yang akan mengurangi jumlah kode yang harus Anda tulis jika Anda memanfaatkannya?

Jawaban:

190

Untuk satu hal, itu lebih masuk akal dalam hal warisan. Fakta yang Stackmeluas Vectorbenar-benar aneh, dalam pandangan saya. Di awal Jawa, pewarisan IMO terlalu sering digunakan - Propertiesmenjadi contoh lain.

Bagi saya, kata penting dalam dokumen yang Anda kutip konsisten . Dequememperlihatkan serangkaian operasi yang semuanya tentang kemampuan untuk mengambil / menambah / menghapus item dari awal atau akhir koleksi, iterate dll - dan hanya itu. Sengaja tidak ada cara untuk mengakses elemen dengan posisi, yang Stackmengekspos karena itu adalah subkelas dari Vector.

Oh, dan juga Stacktidak memiliki antarmuka, jadi jika Anda tahu Anda perlu Stackoperasi, Anda akhirnya berkomitmen ke kelas konkret tertentu, yang biasanya bukan ide yang baik.

Juga seperti yang ditunjukkan dalam komentar, Stackdan Dequememiliki perintah iterasi terbalik:

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

yang juga dijelaskan dalam JavaDocs for Deque.iterator () :

Mengembalikan iterator atas elemen-elemen dalam deque ini dalam urutan yang tepat. Elemen-elemen akan dikembalikan secara berurutan dari pertama (kepala) ke yang terakhir (ekor).

Jon Skeet
sumber
10
javadoc dari ArrayDeque mengatakan "Kelas ini cenderung lebih cepat dari Stack ketika digunakan sebagai stack, dan lebih cepat dari LinkedList ketika digunakan sebagai antrian." ..Bagaimana cara menentukan apakah saya ingin menggunakan ini sebagai tumpukan atau sebagai antrian?
Geek
23
@ Geek: Kamu tidak. Intinya adalah bahwa jika Anda ingin perilaku antrian, Anda bisa menggunakan LinkedList, tetapi ArrayDequeueakan (sering) menjadi lebih cepat. Jika Anda ingin perilaku stack, Anda bisa menggunakan Stacktetapi ArrayDequeakan (sering) lebih cepat.
Jon Skeet
7
Bukankah itu kurang masuk akal dalam hal abstraksi? Maksud saya, tidak ada solusi yang benar-benar bagus dalam hal abstraksi, karena Stackmemiliki masalah eksposur rep, tetapi jika saya ingin struktur data stack saya ingin dapat memanggil metode seperti push, pop, dan mengintip, dan bukan hal-hal yang harus lakukan dengan ujung tumpukan lainnya.
PeteyPabPro
4
@JonSkeet juga iterator Stack salah, karena iterates dari bawah ke atas, bukan dari atas ke bawah. stackoverflow.com/questions/16992758/…
Pavel
1
@PeteyPabPro: Anda benar. Menggunakan Dequeue sebagai tumpukan masih memungkinkan penggunaan non-LIFO sebagai metode Vektor bawaan dari Stack. Penjelasan tentang masalah dan solusi (dengan enkapsulasi) dapat ditemukan di sini: baddotrobot.com/blog/2013/01/10/stack-vs-deque
rics
4

Ini adalah interpretasi saya tentang inkonsistensi yang disebutkan dalam deskripsi kelas Stack.

Jika Anda melihat Implementasi Tujuan Umum di sini - Anda akan melihat ada pendekatan yang konsisten untuk implementasi set, peta, dan daftar.

  • Untuk mengatur dan memetakan kami memiliki 2 implementasi standar dengan peta hash dan pohon. Yang pertama paling banyak digunakan dan yang kedua digunakan ketika kita membutuhkan struktur yang dipesan (dan juga mengimplementasikan antarmuka sendiri - SortedSet atau SortedMap).

  • Kami dapat menggunakan gaya pernyataan yang disukai seperti Set<String> set = new HashSet<String>();melihat alasan di sini .

Tetapi kelas Stack: 1) tidak memiliki antarmuka sendiri; 2) adalah subkelas dari kelas Vector - yang didasarkan pada array yang dapat diubah ukurannya; jadi di mana penerapan daftar tertaut dari stack?

Dalam antarmuka Deque kami tidak memiliki masalah seperti itu termasuk dua implementasi (array yang dapat diubah-ubah - ArrayDeque; daftar tertaut - LinkedList).

irudyak
sumber
2

Satu lagi alasan untuk menggunakan Dequeue over Stack adalah Dequeue memiliki kemampuan untuk menggunakan stream convert to list dengan menjaga konsep LIFO diterapkan sementara Stack tidak.

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]
Amer Qarabsa
sumber
2

Berikut adalah beberapa alasan mengapa Deque lebih baik daripada Stack:

Desain berorientasi objek - Warisan, abstraksi, kelas dan antarmuka: Stack adalah sebuah kelas, Deque adalah sebuah antarmuka. Hanya satu kelas yang dapat diperluas, sedangkan sejumlah antarmuka dapat diimplementasikan oleh satu kelas di Jawa (multiple inheritance of type). Menggunakan antarmuka Deque menghilangkan ketergantungan pada kelas Stack beton dan leluhurnya dan memberi Anda lebih banyak fleksibilitas, misalnya kebebasan untuk memperluas kelas yang berbeda atau menukar implementasi Deque yang berbeda (seperti LinkedList, ArrayDeque).

Inkonsistensi: Stack memperluas kelas Vector, yang memungkinkan Anda untuk mengakses elemen dengan indeks. Ini tidak konsisten dengan apa yang seharusnya dilakukan oleh Stack, itulah mengapa antarmuka Deque lebih disukai (tidak memungkinkan operasi semacam itu) - operasi yang diizinkan konsisten dengan apa yang harus diizinkan oleh struktur data FIFO atau LIFO.

Kinerja: Kelas vektor yang diperluas Stack pada dasarnya adalah versi "thread-safe" dari ArrayList. Sinkronisasi berpotensi menyebabkan hit kinerja signifikan ke aplikasi Anda. Juga, memperluas kelas-kelas lain dengan fungsi yang tidak dibutuhkan (seperti yang disebutkan dalam # 2) mengasapi objek Anda, berpotensi menghabiskan banyak memori tambahan dan overhead kinerja.

Arpit Bhargava
sumber
-1

Bagi saya poin spesifik ini tidak ada: Stack adalah Threadsafe karena berasal dari Vector, sedangkan implementasi deque paling tidak, dan dengan demikian lebih cepat jika Anda hanya menggunakannya dalam satu utas.

Dan saya
sumber
grep "Terlepas dari ini"
Pacerier
-1

Kinerja mungkin menjadi alasan. Algoritma yang saya gunakan turun dari 7,6 menit menjadi 1,5 menit dengan hanya mengganti Stack dengan Deque.

eddy
sumber
grep "Terlepas dari ini"
Pacerier
-6

Deque digunakan adalah situasi di mana Anda ingin mengambil elemen dari kepala dan ekor. Jika Anda ingin tumpukan sederhana tidak perlu pergi untuk deque.

Tepi
sumber
1
silakan lihat pragraf terakhir dalam pertanyaan. Javadoc mengatakan Deques harus digunakan dan saya ingin tahu mengapa?
Geek
2
Deque lebih disukai karena Anda tidak dapat mengakses elemen di tengah. Ini berakhir ganda, jadi bisa jadi FILO untuk FIFO.
Thufir
Saya pikir apa yang ingin mereka katakan adalah bahwa kelas mendukung operasi Stack sebagai metode eksplisit. Lihat dokumentasi dan metodenya. Ini memiliki dukungan eksplisit untuk menerapkan Stack.
Abhishek Nandgaonkar