เหตุใดฉันจึงควรใช้ Deque over Stack?


157

ฉันต้องการStackโครงสร้างข้อมูลสำหรับกรณีการใช้งานของฉัน ฉันควรจะสามารถผลักรายการในโครงสร้างข้อมูลและฉันต้องการดึงรายการสุดท้ายจากกอง JavaDoc สำหรับกองพูดว่า:

ชุดการดำเนินการสแต็ก LIFO ที่สมบูรณ์และสอดคล้องกันมากขึ้นถูกจัดเตรียมโดยอินเตอร์เฟส Deque และการประยุกต์ใช้ซึ่งควรใช้ในการกำหนดค่าตามความชอบของคลาสนี้ ตัวอย่างเช่น:

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

ฉันไม่ต้องการพฤติกรรมการซิงโครไนซ์ที่นี่อย่างแน่นอนเพราะฉันจะใช้โครงสร้างข้อมูลนี้กับวิธีการ นอกจากนี้ทำไมฉันถึงชอบที่นี่Dequeมากกว่าStack

PS: javadoc จาก Deque พูดว่า:

Deques สามารถใช้เป็นกองซ้อน LIFO (Last-In-First-Out) ควรใช้อินเทอร์เฟซนี้ในการกำหนดลักษณะคลาสสแต็กดั้งเดิม


1
มันมีวิธีการอบมากกว่าหรือ "ชุดที่สมบูรณ์และสอดคล้องกัน" ซึ่งจะลดจำนวนของรหัสที่คุณต้องเขียนถ้าคุณใช้ประโยชน์จากพวกเขา?

คำตอบ:


190

สำหรับสิ่งหนึ่งมันมีเหตุผลมากขึ้นในแง่ของการสืบทอด ในความคิดของฉันที่StackขยายออกไปVectorนั้นแปลกจริงๆ ในช่วงต้นของ Java มรดกถูกใช้มากเกินไป IMO -Propertiesเป็นอีกตัวอย่างหนึ่ง

สำหรับผมคำสำคัญในเอกสารที่คุณยกมาเป็นที่สอดคล้องกัน Dequeแสดงชุดของการดำเนินการซึ่งทั้งหมดเกี่ยวกับความสามารถในการดึง / เพิ่ม / ลบรายการจากจุดเริ่มต้นหรือจุดสิ้นสุดของคอลเลกชันทำซ้ำ ฯลฯ - และนั่นคือมัน มีเจตนาที่ไม่มีทางที่จะเข้าถึงองค์ประกอบโดยตำแหน่งซึ่งStackหมายความว่าเพราะมันเป็น subclass Vectorของ

โอ้และStackไม่มีอินเทอร์เฟซดังนั้นถ้าคุณรู้ว่าคุณต้องการStackการดำเนินการคุณต้องยอมรับในระดับคอนกรีตเฉพาะซึ่งไม่ใช่ความคิดที่ดี

นอกจากนี้ยังชี้ให้เห็นในความคิดเห็นStackและDequeมีคำสั่งซ้ำซ้ำ:

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

ซึ่งอธิบายไว้ใน JavaDocs สำหรับDeque.iterator () :

ส่งคืนตัววนซ้ำกับองค์ประกอบใน deque นี้ตามลำดับที่เหมาะสม องค์ประกอบจะถูกส่งกลับตามลำดับตั้งแต่แรก (หัว) ถึงสุดท้าย (ท้าย)


10
javadoc ของ ArrayDeque กล่าวว่า "คลาสนี้น่าจะเร็วกว่าสแต็กเมื่อใช้เป็นสแต็กและเร็วกว่า LinkedList เมื่อใช้เป็นคิว" .. ฉันจะระบุได้อย่างไรว่าฉันตั้งใจจะใช้สิ่งนี้เป็นสแต็กหรือเป็นคิว?
Geek

23
@Geek: คุณทำไม่ได้ ประเด็นก็คือถ้าคุณต้องการพฤติกรรมการเข้าคิวคุณสามารถใช้LinkedListแต่ArrayDequeueจะเร็วกว่า (บ่อยครั้ง) หากคุณต้องการพฤติกรรมสแต็กคุณสามารถใช้Stackแต่ArrayDequeจะ (มัก) จะเร็วขึ้น
Jon Skeet

7
มันไม่สมเหตุสมผลหรือไม่ในแง่ของสิ่งที่เป็นนามธรรม? ฉันหมายความว่าการแก้ปัญหาไม่ดีในแง่ของนามธรรมเพราะStackมีปัญหาการสัมผัสตัวแทน แต่ถ้าฉันต้องการโครงสร้างข้อมูลแบบสแต็คฉันต้องการที่จะสามารถเรียกวิธีการต่างๆเช่น push, pop, peek และไม่ใช่สิ่งที่ต้องทำ ทำกับส่วนอื่น ๆ ของสแต็ค
PeteyPabPro

4
@JonSkeet iterator ของ Stack ยังผิดเพราะมันวนซ้ำจากล่างขึ้นบนแทนการเรียงจากบนลงล่าง stackoverflow.com/questions/16992758/…
Pavel

1
@PeteyPabPro: คุณพูดถูก การใช้ Dequeue เป็นสแต็กยังอนุญาตการใช้งานที่ไม่ใช่ LIFO เป็นวิธี Vector ที่สืบทอดของ Stack คำอธิบายของปัญหาและวิธีการแก้ปัญหา (มีการห่อหุ้ม) สามารถพบได้ที่นี่: baddotrobot.com/blog/2013/01/10/stack-vs-deque
RICS

4

นี่คือการตีความของฉันของความไม่สอดคล้องกันที่กล่าวถึงในคำอธิบายของชั้นสแต็ค

หากคุณดูการใช้งานทั่วไปที่นี่ - คุณจะเห็นว่ามีวิธีการที่สอดคล้องกับการใช้งานชุดแผนที่และรายการ

  • สำหรับชุดและแผนที่เรามีการใช้งานมาตรฐาน 2 แบบพร้อมแผนที่แฮชและต้นไม้ อันแรกถูกใช้มากที่สุดและอันที่สองถูกใช้เมื่อเราต้องการโครงสร้างแบบสั่ง (และยังใช้อินเทอร์เฟซของตัวเอง - SortedSet หรือ SortedMap)

  • เราอาจใช้รูปแบบที่ต้องการในการประกาศเช่น Set<String> set = new HashSet<String>();เหตุผลที่เห็นนี่

แต่คลาสสแต็ก: 1) ไม่มีอินเทอร์เฟซของตัวเอง 2) เป็นคลาสย่อยของคลาสเวกเตอร์ซึ่งขึ้นอยู่กับอาร์เรย์ที่ปรับขนาดได้ ดังนั้นการเชื่อมโยงการใช้งานรายการสแต็คอยู่ที่ไหน

ในอินเทอร์เฟซ Deque เราไม่มีปัญหาดังกล่าวรวมถึงการใช้งานสองแบบ (อาร์เรย์ที่ปรับขนาดได้ - ArrayDeque; Linked list - LinkedList)


2

อีกเหตุผลหนึ่งที่ควรใช้ Dequeue over Stack คือ Dequeue มีความสามารถในการใช้สตรีมที่แปลงเป็นลิสต์โดยคงแนวคิด LIFO ไว้ในขณะที่สแต็กไม่ทำงาน

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 ดีกว่า Stack:

การออกแบบเชิงวัตถุ - มรดก, นามธรรม, คลาสและอินเทอร์เฟซ: Stack เป็นคลาส Deque เป็นอินเทอร์เฟซ สามารถขยายคลาสได้เพียงคลาสเดียวในขณะที่จำนวนอินเตอร์เฟสใด ๆ สามารถใช้งานได้โดยคลาสเดียวใน Java (การสืบทอดหลายประเภท) การใช้อินเทอร์เฟซ Deque จะลบการพึ่งพาคลาสคอนกรีตสแต็กและบรรพบุรุษของมันและช่วยให้คุณมีความยืดหยุ่นมากขึ้นเช่นอิสระในการขยายคลาสที่แตกต่างกันหรือสลับการใช้ Deque (เช่น LinkedList, ArrayDeque)

ความไม่สอดคล้องกัน: สแต็กขยายคลาส Vector ซึ่งอนุญาตให้คุณเข้าถึงองค์ประกอบตามดัชนี นี่คือสิ่งที่ไม่สอดคล้องกับสิ่งที่สแต็คควรทำจริง ๆ ซึ่งเป็นเหตุผลว่าทำไมอินเตอร์เฟซ Deque จึงเป็นที่ต้องการ (ไม่อนุญาตการดำเนินการดังกล่าว) - การดำเนินการที่ได้รับอนุญาตนั้นสอดคล้องกับโครงสร้างข้อมูล FIFO หรือ LIFO

ประสิทธิภาพการทำงาน: คลาส Vector ที่สแต็กขยายนั้นเป็นเวอร์ชั่น "เธรดที่ปลอดภัย" ของ ArrayList การซิงโครไนซ์อาจทำให้ประสิทธิภาพของโปรแกรมของคุณสูง นอกจากนี้ยังขยายคลาสอื่น ๆ ด้วยฟังก์ชั่นที่ไม่จำเป็น (ดังที่กล่าวไว้ใน # 2) ขยายวัตถุของคุณซึ่งอาจทำให้มีหน่วยความจำเพิ่มเติมและค่าใช้จ่ายด้านประสิทธิภาพจำนวนมาก


-1

สำหรับฉันแล้วจุดนี้หายไปโดยเฉพาะ: Stack เป็น Threadsafe เนื่องจากได้มาจาก Vector ในขณะที่การใช้งานแบบถอนการติดตั้งส่วนใหญ่ไม่ได้ใช้และจะเร็วขึ้นหากคุณใช้ในเธรดเดียวเท่านั้น


grep "นอกจากนี้"
Pacerier

-1

ประสิทธิภาพอาจเป็นเหตุผล อัลกอริทึมที่ฉันใช้ลดลงจาก 7.6 นาทีเป็น 1.5 นาทีโดยเพียงแทนที่ Stack ด้วย Deque


grep "นอกจากนี้"
Pacerier

-6

ใช้ Deque คือสถานการณ์ที่คุณต้องการดึงองค์ประกอบจากทั้งส่วนหัวและส่วนท้าย หากคุณต้องการสแต็กเรียบง่ายไม่จำเป็นต้องไปหา deque


1
โปรดดูย่อหน้าสุดท้ายในคำถาม javadoc บอกว่าควรใช้ Deques และฉันต้องการรู้ว่าทำไม
Geek

2
ควรใช้ Deque เนื่องจากคุณไม่สามารถเข้าถึงองค์ประกอบที่อยู่ตรงกลาง มันสิ้นสุดลงสองครั้งดังนั้นสามารถเป็น FILO สำหรับ FIFO
Thufir

ฉันคิดว่าสิ่งที่พวกเขาตั้งใจจะพูดคือชั้นเรียนรองรับการดำเนินการสแต็กเป็นวิธีการที่ชัดเจน ดูเอกสารและวิธีการ มีการสนับสนุนที่ชัดเจนสำหรับการใช้งานสแต็ก
Abhishek Nandgaonkar
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.