เหตุใด ArrayDeque จึงดีกว่า LinkedList


161

ฉันพยายามที่จะเข้าใจว่าทำไม ArrayDeque ของ Java จึงดีกว่า LinkedList ของ Javaเนื่องจากทั้งสองใช้ส่วนติดต่อ Deque

ฉันแทบจะไม่เห็นใครใช้ ArrayDeque ในรหัสของพวกเขา หากมีใครให้ความกระจ่างเกี่ยวกับวิธีการใช้ ArrayDeque มากขึ้นมันจะมีประโยชน์

ถ้าฉันเข้าใจฉันจะมั่นใจในการใช้มันมากขึ้น ฉันไม่เข้าใจการใช้ JDK อย่างชัดเจนเกี่ยวกับวิธีการจัดการการอ้างอิงส่วนหัวและส่วนท้าย


5
ดูคำตอบในคำถามที่ฉันทำเมื่อ
วานนี้

1
ในโฟลเดอร์ที่คุณได้ติดตั้ง JDK src.zipของคุณมีไฟล์ เป็นไฟล์เก็บถาวรที่มีซอร์สโค้ดของคลาส Java ฉันแนะนำอย่างยิ่งให้ศึกษาโครงสร้างของคลาสและภายในเพื่อทำความเข้าใจให้ดีขึ้นว่าคลาส Java ทำงานอย่างไร

คำตอบ:


155

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

หากคุณต้องการเพิ่ม / ลบปลายทั้งสองข้าง ArrayDeque นั้นดีกว่ารายการที่ลิงก์ การเข้าถึงแบบสุ่มแต่ละองค์ประกอบยังเป็น O (1) สำหรับคิวแบบวนรอบ

การดำเนินการที่ดีขึ้นเท่านั้นของรายการที่เชื่อมโยงคือการลบองค์ประกอบปัจจุบันในระหว่างการทำซ้ำ


57
ข้อแตกต่างที่ควรทราบ: LinkedList รองรับองค์ประกอบที่เป็นโมฆะในขณะที่ ArrayDeque ไม่รองรับ
ลุค Usherwood

15
ข้อเสียเล็ก ๆ น้อย ๆ อีกอัน (สำหรับแอปพลิเคชั่นตามเวลาจริง) ก็คือในการดำเนินการแบบกด / เพิ่มนั้นจะใช้เวลาเพิ่มขึ้นเล็กน้อยเมื่ออาร์เรย์ภายในของ ArrayDeque เต็มเนื่องจากมีขนาดเป็นสองเท่าและคัดลอกข้อมูลทั้งหมด
อังเดรฉัน

4
@ AndreiI นี่เป็นเพียงด้านเดียวของเรื่อง แม้ว่าคุณจะไม่รวมค่าใช้จ่ายการทำซ้ำสำหรับแอปพลิเคชันแบบเรียลไทม์และความสามารถในการจัดสรรความจุที่ต้องการล่วงหน้า GC อาจจำเป็นต้องทำซ้ำทั้งรายการ LinkedList โดยทั่วไปคุณกำลังย้ายค่าใช้จ่าย (ซึ่งสูงกว่าในการบูต) เป็น GC
bestsss

3
@DavidT b / c เกี่ยวข้องกับค่าใช้จ่าย GC ของโหนดที่ว่างการกำหนดส่วนหัวอาจจำเป็นต้องมีการทำเครื่องหมายบัตร (สำหรับ GC อีกครั้งหาก LinkedList มีอยู่ใน gen ที่มีการครอบครองแล้ว) ... และอยู่ด้านบนของการเสริมทางอ้อม (แคช - พลาด) เพื่อส่งคืนองค์ประกอบและเชื่อมโยงอีกครั้ง
bestsss

1
นอกจากนี้ยังLinkedListดำเนินการListในขณะที่ArrayDequeไม่ ซึ่งหมายความว่าLinkedListมีวิธีการเช่นindexOfหรือremove(int)ในขณะที่ArrayDequeไม่ได้ บางครั้งอาจมีความสำคัญ
ZhekaKozlov

60

ฉันเชื่อว่าคอขวดของประสิทธิภาพหลักLinkedListคือเมื่อใดก็ตามที่คุณผลักดันไปยังจุดสิ้นสุดของ deque ใด ๆ เบื้องหลังการใช้งานจะจัดสรรโหนดรายการที่เชื่อมโยงใหม่ซึ่งเกี่ยวข้องกับ JVM / OS เป็นหลักและมีราคาแพง นอกจากนี้เมื่อใดก็ตามที่คุณโผล่ออกมาจากปลายใด ๆ โหนดภายในของLinkedListกลายเป็นสิทธิ์ในการเก็บขยะและที่ทำงานเบื้องหลัง นอกจากนี้เนื่องจากโหนดรายการที่เชื่อมโยงได้รับการจัดสรรที่นี่และที่นั่นการใช้งานแคช CPU จะไม่ให้ประโยชน์มากนัก

หากมันอาจเป็นที่สนใจฉันมีหลักฐานว่าการเพิ่ม (ต่อท้าย) องค์ประกอบไปยังArrayListหรือArrayDequeทำงานในเวลาคงที่ตัดจำหน่าย; อ้างถึงนี้


26

ArrayDeque ใหม่กับ Java 6 ซึ่งเป็นสาเหตุของรหัสจำนวนมาก (โดยเฉพาะโครงการที่พยายามใช้งานร่วมกับ Java เวอร์ชันก่อนหน้านี้) ไม่ได้ใช้งาน

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


17

ทุกคนวิจารณ์LinkedList, คิดเกี่ยวกับคนอื่น ๆ ที่ใช้ Listใน Java อาจใช้ArrayListและLinkedListส่วนใหญ่เพราะพวกเขาก่อนหน้า Java 6 และเพราะคนเหล่านั้นเป็นคนที่ได้รับการสอนเป็นจุดเริ่มต้นในหนังสือส่วนใหญ่

แต่นั่นไม่ได้หมายความว่าผมจะสุ่มสี่สุ่มห้าใช้LinkedList'หรือArrayDeque' s ด้าน หากคุณต้องการที่จะรู้ว่าจะดูที่มาตรฐานด้านล่างทำโดยไบรอัน

การตั้งค่าการทดสอบจะพิจารณาว่า:

  • แต่ละวัตถุทดสอบคือสตริง 500 อักขระ แต่ละสตริงเป็นวัตถุที่แตกต่างในหน่วยความจำ
  • ขนาดของอาร์เรย์ทดสอบจะมีการเปลี่ยนแปลงระหว่างการทดสอบ
  • สำหรับแต่ละชุดขนาด / ชุดคิวคิวการใช้งานจะทำการทดสอบ 100 ครั้งและคำนวณเวลาเฉลี่ยต่อการทดสอบ
  • การทดสอบแต่ละครั้งประกอบด้วยการเติมแต่ละคิวด้วยวัตถุทั้งหมดจากนั้นนำออกทั้งหมด
  • วัดเวลาในรูปของมิลลิวินาที

ผลการทดสอบ:

  • ต่ำกว่า 10,000 องค์ประกอบทั้ง LinkedList และ ArrayDeque ทดสอบโดยเฉลี่ยที่ระดับ 1 มิลลิวินาที
  • เมื่อชุดข้อมูลมีขนาดใหญ่ขึ้นความแตกต่างระหว่างเวลาทดสอบ ArrayDeque และ LinkedList โดยเฉลี่ยจะใหญ่ขึ้น
  • ที่ขนาดการทดสอบ 9,900,000 องค์ประกอบวิธี LinkedList ใช้เวลานานกว่าวิธี ArrayDeque ประมาณ 165%

กราฟ:

ป้อนคำอธิบายรูปภาพที่นี่

Takeaway:

  • หากความต้องการของคุณคือการจัดเก็บองค์ประกอบ 100 หรือ 200 มันจะไม่สร้างความแตกต่างมากโดยใช้ทั้งคิว
  • อย่างไรก็ตามหากคุณกำลังพัฒนาบนอุปกรณ์พกพาคุณอาจต้องการใช้ ArrayListหรือArrayDequeคาดเดาความจุสูงสุดที่รายการนั้นอาจจำเป็นต้องใช้เนื่องจากข้อ จำกัด ของหน่วยความจำที่เข้มงวด
  • มีโค้ดจำนวนมากเขียนโดยใช้LinkedListดอกยางอย่างระมัดระวังเมื่อตัดสินใจใช้ArrayDequeโดยเฉพาะอย่างยิ่งเพราะไม่ได้ใช้Listอินเทอร์เฟซ (ฉันคิดว่ามันมีเหตุผลใหญ่พอ) อาจเป็นได้ว่าการเจรจา codebase ArrayDequeของคุณกับอินเตอร์เฟซรายการอย่างกว้างขวางมากที่สุดอาจและคุณตัดสินใจที่จะกระโดดเข้ากับ การใช้สำหรับการใช้งานภายในอาจเป็นความคิดที่ดี ...

เกณฑ์มาตรฐานนี้จะจับเวลา GC ที่เกิดจากขยะในรายการที่เชื่อมโยงได้อย่างไร
0xbe5077ed

3

ArrayDequeและLinkedListกำลังใช้งานDeque interface แต่การนำไปใช้นั้นแตกต่างกัน

ความแตกต่างที่สำคัญ:

  1. ArrayDequeระดับคือการดำเนินงานที่ปรับขนาดได้อาร์เรย์ของDequeอินเตอร์เฟซและLinkedListระดับคือการดำเนินรายการ

  2. องค์ประกอบ NULL สามารถเพิ่มไปยังLinkedListแต่ไม่สามารถอยู่ในArrayDeque

  3. ArrayDequeมีประสิทธิภาพมากกว่าLinkedListสำหรับการเพิ่มและลบการดำเนินการที่ปลายทั้งสองและการใช้งาน LinkedList นั้นมีประสิทธิภาพสำหรับการลบองค์ประกอบปัจจุบันระหว่างการทำซ้ำ

  4. การใช้งานLinkedListใช้หน่วยความจำมากกว่าArrayDeque

ดังนั้นหากคุณไม่ต้องสนับสนุนองค์ประกอบ NULL && มองหาหน่วยความจำน้อย && ประสิทธิภาพของการเพิ่ม / ลบองค์ประกอบที่ปลายทั้งสองArrayDequeเป็นสิ่งที่ดีที่สุด

อ้างถึงเอกสารประกอบสำหรับรายละเอียดเพิ่มเติม


13
"ในรายการที่เชื่อมโยงจะใช้เวลา O (N) เพื่อค้นหาองค์ประกอบสุดท้าย" ไม่เป็นความจริงอย่างแน่นอน LinkedList ถูกนำมาใช้เป็นรายการที่เชื่อมโยงเป็นสองเท่าดังนั้นคุณไม่จำเป็นต้องสำรวจรายการเพื่อรับองค์ประกอบสุดท้าย ( header.previous.element) การเรียกร้อง "ประสิทธิภาพของหน่วยความจำ" ยังสามารถท้าทายได้เนื่องจากอาเรย์สำรองมีการปรับขนาดให้เป็นกำลังต่อไปของ 2 เสมอ
Clément MATHIEU

5
"จะใช้ O (N) เพื่อค้นหาองค์ประกอบสุดท้าย" คือผิด รายการที่เชื่อมโยงจะเก็บการอ้างอิงไปยังโหนดสุดท้ายและ LinkedList.descendingIterator () รับของโหนดนั้น ดังนั้นเราจึงได้ประสิทธิภาพ O (1) ดู: coffeeorientedprogramming.wordpress.com/2018/04/23/… (ดังนั้น downvoting)
Leo Ufimtsev

1
เมื่อคุณใช้การIteratorเข้าถึงองค์ประกอบสุดท้ายการดำเนินการคือ O (N) สำหรับทั้งสองคลาส เมื่อคุณใช้Dequeอินเตอร์เฟสทั่วไปการเข้าถึงองค์ประกอบสุดท้ายคือ O (1) สำหรับทั้งสองคลาส ไม่ว่าคุณจะใช้มุมมองแบบใดการอ้างถึง O (1) ถึงArrayDequeและ O (N) LinkedListในเวลาเดียวกันถือเป็นเรื่องผิด
Holger

ข้อเสนอแนะทั้งหมดของคุณจะได้รับและแก้ไขเนื้อหา
Ravindra babu

1

แม้ว่าArrayDeque<E>และLinkedList<E>มีทั้งการดำเนิน Deque<E>การเชื่อมต่อ แต่ ArrayDeque ใช้พื้นอาร์เรย์วัตถุE[]สำหรับการรักษาองค์ประกอบภายในวัตถุของมันจึงทั่วไปใช้ดัชนีสำหรับตำแหน่งหัวและหางองค์ประกอบ

สรุปแล้วมันใช้งานได้ดีเหมือนกับ Deque (ด้วยวิธี Deque ทั้งหมด) แต่ใช้โครงสร้างข้อมูลของอาร์เรย์ ส่วนที่ดีที่สุดขึ้นอยู่กับว่าคุณใช้มันอย่างไรและที่ไหน


-1

ไม่เป็นเช่นนั้นเสมอไป

ตัวอย่างเช่นในกรณีด้านล่างlinkedlistมีประสิทธิภาพที่ดีกว่าArrayDequeleetcode 103

/**
 * 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

ความซับซ้อนของเวลาสำหรับ ArrayDeque สำหรับการเข้าถึงองค์ประกอบคือ O (1) และสำหรับ LinkList คือ O (N) เพื่อเข้าถึงองค์ประกอบสุดท้าย ArrayDeque ไม่ปลอดภัยสำหรับเธรดดังนั้นการซิงโครไนซ์ด้วยตนเองเป็นสิ่งจำเป็นเพื่อให้คุณสามารถเข้าถึงผ่านเธรดหลาย ๆ เธรดได้เร็วขึ้น


3
หากคุณอ้างถึงLinkedListในภาษาจาวาCollectionก็จะมีการเชื่อมโยงเป็นสองเท่าและมีการเข้าถึงหัวและหางอย่างรวดเร็วดังนั้นการเข้าถึงองค์ประกอบสุดท้ายก็ใช้ O (1)
Maurice

การเข้าถึงองค์ประกอบสุดท้ายใน LinkedList ไม่ใช่ O (N) หากคุณใช้ descendingIterator () จะดำเนินการใน O (1) ดูcoffeeorientedprogramming.wordpress.com/2018/04/23/… (ดังนั้นโปรดโหวต )
Leo Ufimtsev

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